local _ENV = hf_setenv('hhdztable',true)

----------------------------------------------------------------------------------------
local skynet = require "skynet"
local utils = require "utils"
local proxypack = require "proxypack"
local msgdef = require "msgdef"
local pb = require 'protobuf'
local cjson = require "cjson"
local server_def = require "server_def"
local util = require "util"
local table_def = require "table_def"
local sharedata = require "skynet.sharedata"
local error_code = require "error_code"
local reason = require "reason"
local hhdz = require "zhajinhua.hhdz"
local game_def = require "game_def"
local cocall = require "cocall"
local constant = require "constant"
local game_robot = require "zhajinhua.hhdz_robot"
local gen_card = require "zhajinhua.gen_card"
local system_store = require "system_store"
local expand = require("expand") --老游戏功能拓展

----------------------

local table_insert = table.insert
local table_remove = table.remove
local table_sort   = table.sort
local table_unpack = table.unpack
local math_floor = math.floor
local string_format = string.format
local math_abs = math.abs
local math_random = math.random
local math_random_seed = math.randomseed

local REGISTER_CLIENT_FD = 0
local ROBOT_CLIENT_FD = -1
local OFFLINE_CLIENT_FD = -2

local SITDOWN_COINS_REQUIRED = 1000000

--只能坐6个人
local MAX_SEAT_NUM = 6
local EMPTY_SEAT = false

-----------------------游戏状态------------------------
local TABLE_STATUS_WAITTING = 1 --等待准备
local TABLE_STATUS_CHECK_START = 2 --游戏开始
local TABLE_STATUS_BETTING = 3 --开始玩游戏
local TABLE_STATUS_GAMEOVER = 4
local TABLE_STATUS_GAMEOVER_WAIT = 5
local TABLE_STATUS_RESTART = 6
local TABLE_STATUS_REAL_TIME_PAD_BEGIN = 7  --下注开始动画


---------------------牌路显示状态--------------------
local TREND_STATUS_WAIT = 1 --休息状态
local TREND_STATUS_BET = 2 -- 下注状态
local TREND_STATUS_END  = 3 -- 结束状态


--------------------游戏状态时间--------------------
local CLIENT_GAME_STATUS_WAIT_TIME = 3
local CLIENT_GAME_STATUS_BETTING_TIME = 15
local CLIENT_GAME_STATUS_GAMEOVER_TIME = 18

local GAME_REAL_TIME_PAD = 2
local GAME_STATUS_WAIT_TIME = CLIENT_GAME_STATUS_WAIT_TIME
local GAME_STATUS_BETTING_TIME = CLIENT_GAME_STATUS_BETTING_TIME 
local GAME_STATUS_GAMEOVER_TIME = CLIENT_GAME_STATUS_GAMEOVER_TIME
local GAME_REAL_TIME_PAD_END = 2
--------------------------客户端阶段-------------------
local CLIENT_PHASE_WAITING_START = 1
local CLIENT_PHASE_WAITING_BET = 2
local CLIENT_PHASE_BETTING = 3
local CLIENT_PHASE_GAMEOVER = 4

--------------------------------------------------
local CONTROL_RESULT_WIN = 1
local CONTROL_RESULT_LOSE = 2
------------------------------模块初始化-----------------------------------
function init(...)
    -----------------------------数据定义BEGIN---------------------------
    global_configs = nil
    --标记房间所有玩家的clientId 包括观战者的clientId
    table_players = {}
    --管理房间内已经坐下的玩家列表
    player_info_list = {}

    --坐下的玩家的顺序
    ordered_players = {}
    --无座玩家的映射
    noseat_players_list = {}

    curr_locked_uids = {}
    uid_betting_map = {}

    -------------------管理---------------------
    curr_hhdz_instance = nil
    curr_round = nil
    --------------------游戏关服管理--------------------
    closing_server = false 
    stopping_server = false
    curr_status = nil

    --------------------游戏时长管理--------------------
    game_start_wait_time = nil
    game_start_betting_time = nil
    game_start_real_time_pad = nil
    game_start_game_over_time = nil
    game_start_game_over_wait_time = nil
    g_game_result_info = nil
    g_history_list = nil
    g_betting_batch_list = nil
    game_create_time = nil
    ----------------------机器人-----------------------
    robot_manager = {}
    gen_card_instance = nil
    is_write_harvest_open = false
    is_write_harvest_close = false
    -------------------玩家是否受个人概率影响-------------
    tablesvr_id = nil
    self_table_type = nil
    self_table_id = nil
    this_table_gid = nil
    self_table_pattern = nil
    self_game_type = nil
    -----------------------------数据定义END---------------------------
    system_send_award = false 
    last_betting_coins = 0

    record_special_list            = {}       --前20局下注最多的和获胜局数最多的
    player_avg_bet                 = 0        --记录上一局玩家的下注金额均值

    self_game_config = nil

    control_uid = nil           --权重最高的玩家
    control_result = 0          --控制玩家输赢 1:赢 2：输

    --enter_robot_map = {}
    start_coins_map = {}
    
    --触发财神驾到的金额
    caishen_coins_map = {}
    is_curr_have_robot = false
    gm_store_coins_add = 0

    system_result_type = 0 --系统控制类型
    err_result = 0  -- 系统控制结果 0 控制成功  1 系统赢钱 2 随机
    gm_control_uid     = 0 --gm控制玩家uid
    -- 连续投注次数记录
    player_not_bet_num = {}
    robot_last_time = 0 --机器人下一次登录广播时间
    robot_enter_time = {} --机器人登录时间列表
    robot_enter_list = {} -- 机器人登录列表
    hf_set_sentinel_end(_ENV)
end

local handler = {}
local internal = {}

local function trigger_event(action,...)
    for _,AIobj in pairs(robot_manager) do
        local f = AIobj[action]
        if f then
            f(AIobj,...)
        end
    end
end

local function set_position(pos,uid)
    ordered_players[pos] = uid
end

local function clear_position(pos)
    ordered_players[pos] = EMPTY_SEAT
end

local function init_position()
    for i = 1, MAX_SEAT_NUM do
        table_insert(ordered_players, EMPTY_SEAT)
    end 
end

local function load_config()
    local attr = {}
    global_configs = nil
    global_configs = setmetatable({},{
        __index = function(t,k) 
            if attr[k] then
                return attr[k]
            else
                local v = sharedata.deepcopy(k)
                attr[k] = v
                return v
            end
        end
    })
    _G.global_configs = global_configs
end

local function send_to_gateway(uid,client_fd,...)
    if client_fd <= 0 then
        return
    end
    return utils.send_to_gateway(0,uid,client_fd,...)
end

local function notify_others(action,excepted_uid,msg)
    --先广播给桌子上的人
    for uid, fd in pairs(table_players) do
        if uid ~= excepted_uid then
            send_to_gateway(uid,fd,action,msg)
        end
    end
end

local function notify_all(action,msg)
    return notify_others(action,nil,msg)
end

local function get_response_player_info(player_info)
    return {
        uid = player_info.uid,
        name = player_info.name,
        coins = player_info.coins,
        icon = player_info.icon,
        position = player_info.position,
        sex = player_info.sex,
        vip_level = player_info.vip_level,
        icon_border = player_info.icon_border,
        win_count = player_info.win_count or 0,
        total_bet = player_info.total_bet or 0,
        play_count = player_info.play_count or 0,
    }
end

function check_robot_ntf_enter(curr_time)
    if robot_last_time and curr_time < robot_last_time then return end
    
    --CD时间
    for uid,v in pairs(robot_enter_time) do
        if v < curr_time - 60*30 then
            robot_enter_time[uid] = nil 
        end
    end

    dbglog(robot_enter_list,robot_enter_time)

    local enter_robot_id
    local count = #robot_enter_list
    for i = 1,count do
        local uid = table_remove(robot_enter_list)
        if not robot_enter_time[uid] then
            enter_robot_id = uid 
            break
        end
    end

    if not enter_robot_id then
        dbglog("enter_robot_id:",enter_robot_id)
        return 
    end

    local play_effect = false
    local player_info = player_info_list[enter_robot_id]

    if not player_info then
        dbglog("enter_robot_id:",enter_robot_id)
        return 
    end
    dbglog("player_info:",player_info)
    
    local player = get_response_player_info(player_info)

    local play_effect = false
    local enter_word = ""
    local vip_level_conf = utils.getDataBykv(global_configs.vip,"level",player_info.vip_level or 0)
    if vip_level_conf and vip_level_conf.privilege[game_def.VIP_PRIVILEGE_TYPE_SIX] then
        play_effect = true
        enter_word = vip_level_conf.enter_word or ""
        robot_enter_time[enter_robot_id] = curr_time
        robot_last_time = curr_time + math_random(60,120)
        notify_all('honghei.NTF_PLAYER_ENTER',{play_effect = play_effect,player = player,enter_word = enter_word})
        dbglog(enter_word,enter_robot_id)
    end 
end

--把输赢发送到好友服(平台游戏概况)
local function sendLoseWinToFriendServer(system_lose_win)  
    if system_lose_win == 0 then return end             
    R().friendsvr(1):send('.info_mgr', 'set_room_summary', self_table_type, {table_type = self_table_type, lose_win = system_lose_win})
end  

local function get_extra_bill_info(uid)
    local t = {game_type=self_game_type,table_type=self_table_type}
    local player_info = player_info_list[uid]
    if not player_info then
        return t
    end

    t.client_version = player_info.client_version
    t.channel = player_info.channel
    t.cur_channel = player_info.cur_channel
    t.device_id = player_info.device_id
    t.device_brand = player_info.device_brand
    t.os_system = player_info.os_system
    t.platform = player_info.platform
    
    return t
end

--获取当前桌上的玩家数量
local function get_player_num()
    local num = 0
    for _,v in ipairs(ordered_players) do
        if v then
            num = num + 1
        end
    end
    return num + #noseat_players_list
end

local function watch_session(game_session,uid,observing)
    if game_session <= 0 then
        return
    end
    local gateway_id = game_session >> 31
    local request
    if observing then
        request = 'observe_fd'
    else
        request = 'unobserve_fd'
    end

    R().gateway(gateway_id):send('.watchdog','tablesvr',request,R().get_source(),game_session,uid)
end

local function add_to_noseat(uid)
    table_insert(noseat_players_list,uid)
    return true
end

local function is_robot(uid)
    return robot_manager[uid] and true
end

local function remove_from_noseat(uid)
    local noseat_idx
    for i,suid in ipairs(noseat_players_list) do
        if suid == uid then
            noseat_idx = i
            break
        end
    end

    if noseat_idx then
        table_remove(noseat_players_list,noseat_idx)
    end
end

--通知所有玩家有人坐下
local function notify_player_sit(player_info)
    local player = {
        uid = player_info.uid,
        name = player_info.name,
        position = player_info.position,
        coins = player_info.coins,
        icon = player_info.icon,
        vip_level = player_info.vip_level,
        icon_border = player_info.icon_border,
    }
    notify_all('honghei.NTF_SITDOWN',{player = player})
end

-- 设置玩家站起
local function stand_up(uid,pos)
    local player_info = assert(player_info_list[uid],
        'stand_up no player info' .. tostring(uid))

    if player_info.position ~= pos then
        return false,411
    end

    if ordered_players[pos] ~= uid then
        return false,410
    end

    clear_position(pos)
    player_info.position = nil
    add_to_noseat(uid)

    notify_all('honghei.NTF_STANDUP',{uid = uid})
end

--设置玩家坐下
local function sit_down(uid,pos)
    if ordered_players[pos] ~= EMPTY_SEAT then
        return false,-401
    end

    local player_info = player_info_list[uid]
    if not player_info then
        return false,-402
    end

    if player_info.position then
        return false,error_code.SEAT_FAIL_HAVE_SEAT
    end
    if pos ~= hhdz.RICH_SEAT_SIDE and  pos ~= hhdz.LUCK_SEAT_SIDE then
        local roomdata = global_configs.roomdata[self_table_type]
        local sit_coins_limit = roomdata.sit_coins_limit or 10000
        if player_info.coins < sit_coins_limit then
            return false, error_code.CANT_SITDOWN_NO_MONEY, sit_coins_limit
        end
    end

    remove_from_noseat(uid)
    set_position(pos,uid)
    player_info.position = pos

    notify_player_sit(player_info)

    return true
end

local function run_robots(new_robot_list)
    for _,robot_obj in pairs(new_robot_list) do
        skynet.fork(function()
            robot_obj:update()
        end)
    end
end
---------------------------------------------------------------------------
local function lock_one_player(uid,table_gid)
    local ok,succ = R().exdbsvr(1):call('.tlock_mgr','set_on_table',uid,table_gid,table_gid)
    if not ok then
        errlog(uid,'failed to set_on_table')
        return
    end

    if not succ then
        return
    end

    curr_locked_uids[uid] = true
    
    return true
end

--玩这实时输赢op
function realTimeWinLose(uid)
    if is_robot(uid) then return end
    local player_info = player_info_list[uid]
    if not player_info.enter_time then 
        errlog("__________player_info.enter_time__是空的____", uid, player_info)
        return 
    end      
    
    local table_gid = player_info.enter_time .. "_" .. self_table_id .."_"..uid
    local leave_time = util:get_now_time()  --真正离开游戏了 
    if player_info.all_win_lose == 0 and player_info.all_free == 0 then 
        --没产生流水就不记录了
        return
    end
    local data = {
        op = "user_leave_game",
        uid = uid,
        table_gid = table_gid, --唯一标记
        table_type = self_table_type,
        name = player_info.name,        
        enter_coins = player_info.enter_coins, --进入时身上金币
        enter_safe_coins = player_info.enter_safe_coins, --进入时保险箱金币 
        enter_time = player_info.enter_time, --进入时间
        leave_time = leave_time, --离开时间
        leave_coins = player_info.coins, --离开时身上金币
        leave_safe_coins = player_info.safe_box, --离开时保险箱金币
        win_lose = player_info.all_win_lose, --在玩游戏内产生的输赢（
        free = player_info.all_free, --玩游戏交上的台费
    }
    dbglog("_______user_leave_game_____",  data)
    billlog(data)
end

local function unlock_one_player(uid,table_gid)
    if is_robot(uid) then return end
    R().exdbsvr(1):send('.tlock_mgr','unset_on_table',uid,this_table_gid)
    curr_locked_uids[uid] = nil
end

local function clear_special_pos_data(uid)
    for _,special_list in pairs(record_special_list) do
        if special_list[uid] then
            special_list[uid] = nil
        end
    end 
end

local function clear_and_unlock_offline_player(uid)
    --dbglog(uid,'now clear this offlined player')
    unlock_one_player(uid,this_table_gid)
    realTimeWinLose(uid)
    local player_info = assert(player_info_list[uid])
    if player_info.position then
        --坐在位置上,则需要站起
        stand_up(uid,player_info.position)
    end

    table_players[uid] = nil
    player_info_list[uid] = nil
    start_coins_map[uid]  = nil
    player_not_bet_num[uid] = nil
    clear_special_pos_data(uid)
    remove_from_noseat(uid)
end

local function is_affected()
    local ok,ret = R().basesvr{key = uid}:call('.msg_handler','is_affected',uid)
    if not ok or not ret then
        return false
    end

    return true
end
---------------------------------------------------------------------------
local function get_player_enter_data(uid)
     --先拉取玩家信息
    local ok,base_data = R().basesvr{key=uid}:call('.msg_handler','get_base_data',uid)
    if not ok then
        errlog(uid,'failed to get base_data',uid)
        return
    end
   -- dbglog(tostring_r(base_data))

    --再拉取玩家的登入信息 跟大厅拉取
    local ok,enter_data = R().hallsvr{key=uid}:call('.msg_handler','get_enter_data',uid)
    if not ok then
        errlog(uid,'failed to get enter_data')
        enter_data = {}
    end
  --  dbglog(tostring_r(enter_data))

    local player_info = {
        uid = base_data.uid,
        name = enter_data.name or '',
        coins = base_data.coins,
        icon = enter_data.icon or '',
        sex = enter_data.sex,
        affect_count = base_data.affect_count,
        affect_rate = base_data.affect_rate,
        vip_level = base_data.vip_level or 0,
        icon_border = enter_data.icon_border,
        safe_box = base_data.safe_box,
    }

    return player_info
end

local function get_betting_list(betting_list,coins)
    local list
    local bet_list

    if betting_list then
        for _, v in pairs(betting_list) do
            local range = utils.str_split(v.coins_range,'-')
            if tonumber(range[1]) and coins >= tonumber(range[1]) then
                if tonumber(range[2]) then
                    if coins < tonumber(range[2]) then
                        list = v.bet_list
                        break
                    end
                else
                    list = v.bet_list
                    break
                end
            end  
        end
    end

    if list then
        bet_list = {}
        for k, v in pairs(list) do  
            bet_list[k] = v
        end
    end
   -- print("========list=====",coins,tostring_r(list))
 
    return bet_list
end

local function get_client_status_and_left_time()
    local curr_phase,next_status_time
    local curr_time = util.get_now_time()
    local left_time
    local status_time = {}

    table_insert(status_time,GAME_STATUS_WAIT_TIME + GAME_REAL_TIME_PAD)
    table_insert(status_time,GAME_STATUS_BETTING_TIME )
    table_insert(status_time,GAME_STATUS_GAMEOVER_TIME + GAME_REAL_TIME_PAD_END)


    if curr_status == TABLE_STATUS_WAITTING or curr_status == TABLE_STATUS_CHECK_START then
        --等待开始中，则没有gamestatus
        curr_phase = TREND_STATUS_WAIT
        next_status_time = game_start_wait_time + GAME_STATUS_WAIT_TIME + GAME_REAL_TIME_PAD
        left_time = next_status_time - curr_time 
    elseif curr_status == TABLE_STATUS_RESTART then
        curr_phase = TREND_STATUS_WAIT 
        left_time = GAME_STATUS_WAIT_TIME + GAME_REAL_TIME_PAD
    elseif curr_status == TABLE_STATUS_REAL_TIME_PAD_BEGIN then
        curr_phase = CLIENT_PHASE_WAITING_BET
        left_time = game_start_real_time_pad - curr_time + GAME_REAL_TIME_PAD        
    elseif curr_status == TABLE_STATUS_BETTING then
        curr_phase = TREND_STATUS_BET
        next_status_time = game_start_betting_time + GAME_STATUS_BETTING_TIME
        left_time = next_status_time - curr_time
    elseif curr_status == TABLE_STATUS_GAMEOVER then
        curr_phase = TREND_STATUS_END
        next_status_time = game_start_game_over_time + GAME_REAL_TIME_PAD_END + GAME_STATUS_GAMEOVER_TIME
        left_time = next_status_time - curr_time
    elseif curr_status == TABLE_STATUS_GAMEOVER_WAIT then
        --结算时间
        curr_phase = TREND_STATUS_END
        next_status_time = game_start_game_over_wait_time + GAME_STATUS_GAMEOVER_TIME
        left_time = next_status_time - curr_time
    else
        errlog('unknown status...',curr_status)
    end

    -- if left_time < 0 then left_time = 0 end

    dbglog("curr_phase:",curr_phase,"left_time",left_time)
    return curr_phase,left_time,status_time
end

local function get_enter_info(uid)
    local rsp_player_info_list = {}
    for _,uid in ipairs(ordered_players) do
        if uid ~= EMPTY_SEAT then
            local player_info = assert(player_info_list[uid])
            local rsp_player_info = get_response_player_info(player_info)
            table_insert(rsp_player_info_list,rsp_player_info)
        end
    end

    local rate_list = {}
    for type,rate in pairs(hhdz.COMPENSATION_RATE) do
        table_insert(rate_list,{type = type,rate = rate})
    end

    local player_info = assert(player_info_list[uid])
    if not player_info.available_betting_list then
        local betting_list = {100,1000,5000,10000,50000}
        local available_betting_list = get_betting_list(global_configs.bet_list.hhdz_bet_list,player_info.coins) or betting_list
        player_info.available_betting_list = available_betting_list
    end

    local curr_phase,next_status_time

    local curr_game_status = {}

    local curr_time = util.get_now_time()
    local left_time

    if curr_status == TABLE_STATUS_WAITTING or curr_status == TABLE_STATUS_CHECK_START then
        --等待开始中，则没有gamestatus
        curr_phase = CLIENT_PHASE_WAITING_START
        next_status_time = game_start_wait_time + GAME_STATUS_WAIT_TIME 
        left_time = next_status_time - curr_time
    elseif curr_status == TABLE_STATUS_RESTART then
        curr_phase = CLIENT_PHASE_WAITING_START
        left_time = GAME_STATUS_WAIT_TIME
    elseif curr_status == TABLE_STATUS_REAL_TIME_PAD_BEGIN then
        curr_phase = CLIENT_PHASE_WAITING_BET
        left_time = GAME_REAL_TIME_PAD
    elseif curr_status == TABLE_STATUS_BETTING or curr_status == TABLE_STATUS_GAMEOVER then
        --押注时间
        local tmp_list = curr_hhdz_instance:get_my_betting_list(uid)
        local my_betting_list = {}
        if tmp_list then
            for side,coins in pairs(tmp_list) do
                table_insert(my_betting_list,{side = side,coins = coins})
            end
        end

        local tmp_list = curr_hhdz_instance:get_all_betting_list()
        local all_betting_list = {}
        for side,coins in pairs(tmp_list) do
            table_insert(all_betting_list,{side = side,coins = coins})
        end

        local tmp_list = curr_hhdz_instance:get_gold_list()
        local all_gold_list = {}
        for side,gold_num in pairs(tmp_list) do
            table_insert(all_gold_list,{side = side,gold_num = gold_num})
        end

        curr_phase = CLIENT_PHASE_BETTING
        curr_game_status.my_betting_list = my_betting_list
        curr_game_status.all_betting_list = all_betting_list
        curr_game_status.all_gold_list = all_gold_list
        if curr_status == TABLE_STATUS_GAMEOVER then  
            left_time = 0
        else
            next_status_time = game_start_betting_time + GAME_STATUS_BETTING_TIME
            left_time = next_status_time - curr_time
        end
        dbglog("enter TABLE_STATUS_BETTING curr_time:",curr_time,curr_status,left_time)
    elseif curr_status == TABLE_STATUS_GAMEOVER_WAIT then
        --结算时间
        curr_phase = CLIENT_PHASE_GAMEOVER
        next_status_time = game_start_game_over_wait_time + GAME_STATUS_GAMEOVER_TIME
        curr_game_status.game_result = g_game_result_info

        left_time = next_status_time - curr_time
    else
        errlog('unknown status...',curr_status)
    end

    if left_time < 0 then left_time = 0 end
    
    local winner_history_list = {}
    local winner_type_list = {}
    for _,h in pairs(g_history_list) do         
        table_insert(winner_history_list,1,h.side)
    end
    for _,h in pairs(g_history_list) do
        table_insert(winner_type_list,1,h.type)
    end
    local roomdata = global_configs.roomdata[self_table_type]
    local min_bet = roomdata.min_bet or 0
    local config ={
        person_limit_min = 0,
        person_limit_max = 0,
    }
    if roomdata.person_limit then
        config.person_limit_min = roomdata.person_limit[1] or 0
        config.person_limit_max = roomdata.person_limit[2] or 0
    end  
    local enter_info = {
        player_info_list = rsp_player_info_list,
        rate_list = rate_list,
        available_betting_list = player_info.available_betting_list,
        curr_phase = curr_phase,
        left_time = left_time,
        game_status = curr_game_status,
        winner_history_list = winner_history_list,
        winner_type_list = winner_type_list,
        --TODO 后面支持配置
        side_betting_limit = {2000000,2000000,2000000},
        personal_side_betting_limit = {2000000,2000000,2000000},
        bet_need_coins = min_bet,
        config = config,
    }

    return {
        game_type = self_game_type,
        table_type = self_table_type,
        honghei_enter_info = enter_info,
        name = roomdata.name,
    }
end

local function enter(uid,client_fd,msg)
    --机器人enter可以直接返回
    if client_fd == ROBOT_CLIENT_FD then
       -- enter_robot_map[uid] = true
        return false
    end

    if not table_players[uid] then
        errlog(uid,'this player had not been registerd!!!')
        return false,-431
    end

    local roomdata = self_game_config
    if not roomdata then
        errlog("unkown table type!!!!",self_table_type)
        return false,-1
    end

    local data = get_player_enter_data(uid)
    if not data then
        errlog(uid,'failed to get_player_enter_data')
        return false,-2
    end

    local player_info = player_info_list[uid]

    if player_info then --玩家如果已经再房间内那么直接那么直接刷新一下玩家数据 这个分支可能是玩家短线重连
        player_info.name = data.name
        player_info.icon = data.icon
        player_info.coins = data.coins
        player_info.affect_count = data.affect_count
        player_info.affect_rate = data.affect_rate
        player_info.vip_level = data.vip_level
        player_info.icon_border = data.icon_border
        player_info.win_count = player_info.win_count or 0 
        player_info.total_bet = player_info.total_bet or 0
        player_info.play_count = player_info.play_count or 0  --玩家的玩牌局数
        player_info.safe_box = data.safe_box
    else --之前没有再房间内也就是首次进入房间
        if get_player_num() >= table_def.game_player_num[self_game_type] then
            return false, error_code.CANNOT_ENTER_ROOM_FULL
        end
        --已经进入过其他房间了
        if not lock_one_player(uid,this_table_gid) then
            errlog(uid,'failed to lock player,you may have been in other table')
            return false,error_code.CANNOT_ENTER_FTABLE_LOCKED
        end
        player_info = data
        player_info.init_coins = player_info.coins
        player_info_list[uid] = player_info
        player_info.enter_coins = data.coins
        player_info.enter_time = os.time()
        player_info.enter_safe_coins = data.safe_box
        player_info.all_win_lose = 0
        player_info.all_free = 0
        player_info.all_produce = 0
        player_info.all_bet = 0

    end
    player_info.os_system = data.os_system
    player_info.platform = data.platform
    table_players[uid] = client_fd

    player_info.client_version = msg.client_version
    player_info.channel = msg.channel
    player_info.cur_channel = msg.cur_channel
    player_info.device_id = msg.device_id
    player_info.device_brand = msg.device_brand

    local rsp_enter_info = get_enter_info(uid)
    dbglog("rsp_enter_info ",tostring_r(rsp_enter_info))
    return true,rsp_enter_info
end

local function set_sit_down()
    --dbglog(tostring_r(player_info_list))
    local roomdata = global_configs.roomdata[self_table_type]
    local sit_coins_limit = roomdata.sit_coins_limit or 10000
    for uid, _ in pairs(player_info_list) do
        if player_info_list[uid] then
            local info = player_info_list[uid]
            local position = info.position
            if uid ~= ordered_players[hhdz.RICH_SEAT_SID] and uid ~= ordered_players[hhdz.LUCK_SEAT_SIDE] then
                if info.coins < sit_coins_limit and position ~= nil and ordered_players[position] ~= EMPTY_SEAT then
                    dbglog("======set_sit_down=====",uid,info.coins,sit_coins_limit)
                    stand_up(uid,info.position)
                    
                --[[  send_to_gateway(uid,table_players[uid],'honghei.RSP_STANDUP',{
                        position = position
                    })--]]
                end
            end
        end
    end
end

local function can_start()
    if curr_hhdz_instance then
        return false,-1
    end    
    local instance = hhdz:new()
    assert(instance:init(gen_card_instance))
    instance:start_game()
    instance:shuffle()
    instance:deal()
    
    return instance
end

local function check_start(curr_time)
    local instance = can_start()
    if not instance then
        dbglog('failed to can_start()',msg)
        return
    end

    print('now start the game...')
    curr_hhdz_instance = instance
    return true
end

local function reduce_coins(uid, money, reason, ...)
    local ok, succ, ret = R().basesvr({ key = uid }):call(".msg_handler", "reduce_coins", uid, money, reason, ...)
    if not ok then
        errlog(string_format("uid(%d) call basesvr reduce coins error", uid))
        return false
    end
    if not succ then
        errlog(string_format("uid(%d) basesvr reduce coins succ == false error", uid))
        return false
    end

    --update_player_coins(uid, ret.curr)
    return ret
end

local function give_winner_coins(uid,coins,output_obj, pay_fee_map)
    local robot = is_robot(uid)
    local ok,succ,ret = R().basesvr{key=uid}:call('.msg_handler',
        'add_coins',uid,coins + pay_fee_map[uid],reason.WIN_COIN,{is_robot=robot},get_extra_bill_info(uid))
    if not ok then
        errlog(uid,'failed to give_winner_coins',coins,succ)
        return
    end
    if not succ then
        errlog(uid,'failed to give_winner_coins',coins)
        return
    end

    local ret = reduce_coins(uid, pay_fee_map[uid], reason.PAY_FEE,get_extra_bill_info(uid))

    if not ret then
        errlog("reduce coins failed",tostring_r(ret) )
        return 
    end 

    -- R().basesvr({key=uid}):send('.msg_handler','add_player_fee_coins',uid,pay_fee_map[uid] or 0)
    -- billlog({op="reducecoins", uid = uid,curr = ret.curr,value = pay_fee_map[uid],is_robot = robot,r = reason.PAY_FEE}, get_extra_bill_info(uid))
    output_obj.curr_coins = ret.curr

    return ret.curr
end

local function get_player_curr_coins(uid)
    if player_info_list[uid] then
        return player_info_list[uid].coins
    else
        local ok,base_data = R().basesvr({key=uid}):call('.msg_handler','get_base_data',uid)
        if not ok then
            errlog(uid,'get_player_curr_coins-->failed to get base_data')
            return 0
        end

        return base_data.coins
    end

    return 0
end

local function get_side_bet_coins(side)
    if not curr_hhdz_instance then
        return 0,0
    end

    local total_side_coins  = curr_hhdz_instance:get_side_betting_total_coins(side)
    local player_side_coins = curr_hhdz_instance:get_side_player_bett_coins(side)

    return total_side_coins,player_side_coins
end

local function get_side_coins_result(player_side_map,side)
    if not player_side_map or not side then
        return 0,0
    end

    local roomdata = global_configs.roomdata[self_table_type]
    local fee_percent = (roomdata.cost or 5)/ 100 

    local total_side_win  = 0
    local player_side_win = 0
    local player_win_list = {}
    for uid,side_info in pairs(player_side_map) do
        for _,data in pairs(side_info) do
            if data.side == side then
                local win_coins = data.result_coins
                total_side_win = total_side_win + win_coins
                if not is_robot(uid) then
                    player_side_win = player_side_win + win_coins
                    table_insert(player_win_list,{uid = uid,win_coins = win_coins})
                end
            end
        end
    end

    return total_side_win,player_side_win,player_win_list
end

local function write_card_record_log(game_result)
    local compensation_map = game_result.compensation_map
    local pay_fee_map = game_result.pay_fee_map
    local winner_coin_map = game_result.winner_coin_map
    local player_side_map = game_result.player_side_map or {}

    -- winner = game_result.winner_side,
    -- type = game_result.compensation_type,
    -- red_type = game_result.red_type,
    -- red_card_list = game_result.red_card_list,
    -- black_type = game_result.black_type,
    -- black_card_list = game_result.black_card_list,
    -- betting_result_list = betting_result_list,

    local winners = {}
    local losers = {}
    local win_list = {}
    local total_num  = 0
    local player_win = 0
    local player_bet_map = curr_hhdz_instance:get_player_bet_coins()
    for uid,coins in pairs(compensation_map) do
        local player_info = player_info_list[uid]
        local left_coins = 0
        if player_info then
            left_coins = player_info.coins
        end
        local add_score = winner_coin_map[uid] - (player_bet_map[uid] or 0)
        if add_score > 0 then
            if not is_robot(uid) then
                table_insert(winners,{ uid = uid, left_score = left_coins, add_score = add_score, 
                pay_fee = pay_fee_map[uid],is_robot = false })
            end
            win_list[uid] = add_score
        else
            if not is_robot(uid) then
                table_insert(losers,{uid = uid, left_score = left_coins, add_score = add_score, 
                pay_fee = pay_fee_map[uid],is_robot = false })
            end
        end
        if not is_robot(uid) then
            player_win = player_win + add_score
        end
        total_num = total_num + 1
    end

    local str_date = os.date("%Y%m%d%H%M%S")
    local have_player_bet = false
    local side_list = {}
    for side = 1,3 do --1红 2黑 3特殊牌型区域
        local side_info = {}
        side_info.side = side

        local card_type = 0
        local cards = {}
        if side == 1 then
            card_type = game_result.red_type
            cards = game_result.red_card_list
        elseif side == 2 then
            card_type = game_result.black_type
            cards =  game_result.black_card_list
        end

        side_info.card_type = card_type
        side_info.cards = cards
        side_info.win = side == game_result.winner_side
        local bet_coins,player_bet_coins = get_side_bet_coins(side)
        side_info.bet_coins = bet_coins
        side_info.player_bet_coins = player_bet_coins

        local total_side_win,player_side_win,player_win_list = get_side_coins_result(player_side_map,side)
        side_info.total_side_win = total_side_win
        side_info.player_side_win = player_side_win

        local player_bet_list = {}
        local side_bet_list = curr_hhdz_instance:get_side_player_bet_list(side)
        for k,v in pairs(side_bet_list) do
            if not is_robot(v.uid) then
                have_player_bet = true
                table_insert(player_bet_list,{uid = v.uid,bet_coins = v.bet_coins})
            end
        end
        side_info.player_bet_list = player_bet_list
        side_info.player_win_list = player_win_list
       
        table_insert(side_list,side_info)
    end

    local exinfo = {
        system_result = system_result_type or 0,
        control_uid = gm_control_uid or 0,
        err_result = err_result or 0,
    }
    if have_player_bet then
        billlog({ op = "card_record", 
            table_gid = str_date .. "_" .. self_table_id, 
            game_type = self_game_type,
            table_type = self_table_type, 
            begin_time = game_start_betting_time,
            end_time = game_start_game_over_time, 
            curr_round = curr_round, 
            password = self_password,
            winner_list = winners, 
            loser_list = losers, 
            system_coins = -player_win,
            address = skynet.address(skynet.self()),
            side_list = side_list,
            exinfo = exinfo,
            total_num = total_num,
        })
    end
    local system_win_lose = 0
    local total_coins = 0
    for uid,bet_coins in pairs(player_bet_map) do
        local add_score = winner_coin_map[uid] - bet_coins + (pay_fee_map[uid] or 0)
        if not is_robot(uid) then
            R().basesvr({key=uid}):send('.msg_handler','add_person_water',uid,bet_coins,is_robot(uid))
            system_win_lose = system_win_lose - winner_coin_map[uid] + bet_coins
            billlog({ op   = "water_history",  
                table_gid  = str_date .. "_" .. self_table_id,
                table_type = self_table_type,
                uid        = uid,
                value      = bet_coins,
                is_robot   = is_robot(uid),
                r          = constant.WATER_TYPE.WATER_BET
            })

            local player_info = player_info_list[uid]
            if player_info then
                local pay_fee = (pay_fee_map[uid] or 0)
                local win_lose = winner_coin_map[uid] - bet_coins
                local produce = winner_coin_map[uid] + pay_fee
                player_info.all_win_lose = player_info.all_win_lose + win_lose
                player_info.all_bet = player_info.all_bet + bet_coins
                player_info.all_produce = player_info.all_produce + produce
                player_info.all_free = player_info.all_free + pay_fee
                R().basesvr({key=uid}):send('.msg_handler','add_total_coins',uid,winner_coin_map[uid] - bet_coins)


                local uid = player_info.uid
                local bet_coins = bet_coins
                local produce = produce
                local win_lose = win_lose
                local free = pay_fee
                expand.sendSummary(self_table_type, uid, bet_coins, produce, win_lose, free)                  
            end

            

            total_coins = total_coins + math_abs(add_score)
            if add_score > 0 then
                R().basesvr({key=uid}):send('.msg_handler','add_today_win_coins',uid,add_score)
            end
            R().basesvr({key=uid}):send('.msg_handler','add_play_count',uid,1)
        end

        -- local curr_coins = get_player_curr_coins(uid)

        -- if not is_robot(uid) and curr_coins <= constant.POCHAN_COINS then --记录破产日志
        --     billlog({op = "bankrupt_register",uid = uid,game_type = self_game_type,
        --         table_type = self_table_type,
        --         ret = curr_coins,
        --     })
        -- end
    end
    sendLoseWinToFriendServer(system_win_lose)
    return total_coins,win_list
end

-- local function add_lose_coins(compensation_map)
--     for uid,coins in pairs(compensation_map) do
--         if coins < 0 then
--              R().basesvr{key=uid}:send('.msg_handler','add_lose_coins',uid,math_abs(coins))
--         end
--     end
-- end

local function sub_lose_coins(winner_coin_map)
    for uid,coins in pairs(winner_coin_map) do
        if not is_robot(uid) then
            R().basesvr{key=uid}:send('.msg_handler','sub_lose_coins',uid,coins)
        end
    end
end

local function update_robot_store(total_coins)
   local roomdata = global_configs.roomdata[self_table_type]
   if not roomdata or not roomdata.open_robot then
        return
   end

   local cnf = global_configs.robot_store[self_table_type]
   if not cnf then
        return
   end

   local fee_rate    = (cnf.fee_rate or 5) / 10000
   local award_rate  = (cnf.award_rate or 5) / 10000
   local fee_coins   = fee_rate * total_coins
   local award_coins = award_rate * total_coins
   
   R().exdbsvr(1):send('.system_coins_mgr','update_store',self_game_type,self_table_type,fee_coins,award_coins)
end

local function check_store_transfer()
    local roomdata = global_configs.roomdata[self_table_type]
    if not roomdata or not roomdata.open_robot then
        return
    end
 
    local cnf = global_configs.robot_store[self_table_type]
    if not cnf then
         return
    end

    local base_min  = cnf.base_min or 0
    local base_max  = cnf.base_max or 0
    local base_normal = cnf.base_normal or 0
    local data = {base_min = base_min,base_max = base_max,base_normal = base_normal,table_type = self_table_type}
    R().exdbsvr(1):send('.system_coins_mgr','check_store_transfer',self_game_type,data)
 end

local function check_today_coins_water_goal(uid,data)
    local seven_reward = global_configs.seven_reward
    local goal_list = {}
    for k,v in pairs(seven_reward) do  
        table_insert(goal_list,(v.coins_low + v.coins_hight)/2)
        table_insert(goal_list,coins_hight)
    end

    local function get_section_and_high(value)
        local section,section_high
        for num,data in pairs(seven_reward) do
            if value >= data.coins_low then
                section = num
                section_high = data.coins_hight
            end 
        end

        return section,section_high
    end
    
    for _,value in pairs(goal_list) do
        if (data.pre_coins_water or 0) < value and (data.curr_coins_water or 0) >= value then
            local section,section_high = get_section_and_high(data.curr_coins_water or 0)
            local ntf = {
                today_coins   = data.curr_coins_water or 0,
                section_count = section,
                section_hight = section_high or 0,
                section_total_count = #seven_reward,
            }
            send_to_gateway(uid,table_players[uid],'hall.NTF_SEVEN_REWARD_GOAL',ntf)
        end
    end
end

local function add_player_coins_water(compensation_map, pay_fee_map)
    for uid,coins in pairs(compensation_map) do
        if not is_robot(uid) then
            if pay_fee_map[uid] then
                coins = coins + pay_fee_map[uid]
            end
            R().basesvr({key = uid}):send('.msg_handler','add_player_coins_water',uid,math_abs(coins))
            -- if ok and ret then
            --     check_today_coins_water_goal(uid,ret)
            -- end
        end
    end
    return
end

local function send_real_horse_message(win_list)
    local roomdata = global_configs.roomdata[self_table_type] or {}
    local exinfo = cjson.encode({game_type = self_game_type})
    for uid, win_coins in pairs(win_list) do
        if not is_robot(uid) and win_coins > 0 then
            local player_info = player_info_list[uid]
            if player_info then
                R().mailsvr(1):send('.opration_message_worker','send_real_horse_message',constant.BROADCAST_IDS.BIDS_HHDZ,
                {name = player_info.name,coin = win_coins,vip_level = player_info.vip_level})

                R().mailsvr(1):send('.opration_message_worker','send_real_horse_message',constant.BROADCAST_IDS.GUIDE_BROAD,
                {vip_level = player_info.vip_level,name = player_info.name,game_name = roomdata.name or '',coin =  win_coins,exinfo = exinfo})
            end
        end
    end
end

local function notify_caishenjiadao(uid,coins)
    print("=============notify_caishenjiadao",uid,coins)
    local curr_time = util.get_now_time()
    local countdown_time = global_configs.caishenjiadao.countdown_time or 10
    local end_time = curr_time + countdown_time + GAME_STATUS_GAMEOVER_TIME --把动画时间加上
    caishen_coins_map[uid] = coins
    send_to_gateway(uid,table_players[uid],'table.NTF_CAISHENJIADAO',
    {end_time = end_time,trigger_coins = coins,game_type = self_game_type})
    billlog({op = "caishenjiadao",type = "trigger",uid = uid,table_type = self_table_type,coins = coins})
end

local function deal_special_pos(uid,pos)
    if not uid then return end 
    --该特殊位置有人
    local sit_uid = ordered_players[pos]
    if sit_uid then
        if sit_uid == uid then   --玩家已经坐在该位置
            return false
        else      --该座位被别人占有，让他起来
            local player_info = player_info_list[sit_uid]
            if player_info and player_info.position then
                stand_up(sit_uid,player_info.position)
            end
        end
    end
    --若玩家在坐下的情况下获得【富豪No.1】和【运气王】的头衔，则自动移动到相应位置
    local player_info = player_info_list[uid]
    if player_info and player_info.position then
        stand_up(uid,player_info.position)
    end

    --玩家入座
    sit_down(uid,pos)
end

local function special_req_sit()
    local list = {}
    for _uid, player_info in pairs(player_info_list) do
        local win_rate = 0
        if player_info.play_count and player_info.play_count ~= 0 then
            win_rate = math_floor((player_info.win_count or 0) / player_info.play_count * 10000)
        end
        table_insert(list,{
            uid = _uid,
            win_count = player_info.win_count or 0,
            total_bet = player_info.total_bet or 0,
            play_count = player_info.play_count or 0,
            win_rate = win_rate,
        })
    end

    if #list == 0 then
        return
    end
    --富豪No.1
    table_sort(list,function(a,b)
        if a.total_bet ~= b.total_bet then
            return a.total_bet > b.total_bet
        else
            return a.win_count > b.win_count
        end
    end)
    --print("==========fffffffffffspecial_req_sit====",tostring_r(list))
    local uid_rich_data = list[1]
    local rich_uid = uid_rich_data.uid

    deal_special_pos(rich_uid,hhdz.RICH_SEAT_SIDE)

    --运气王(获胜局数/玩牌局数 → 下注金 → 获胜局数)
    table_sort(list,function(a,b)
        if a.win_rate ~= b.win_rate then
            return a.win_rate > b.win_rate
        elseif a.total_bet ~= b.total_bet then
            return a.total_bet > b.total_bet
        else
            return a.win_count > b.win_count 
        end
    end)
--    print("==========ffffffspecial_req_sit====",player_avg_bet,tostring_r(list))
    local uid_luck_data = list[1]
    local luck_uid = uid_luck_data.uid
    --若两者玩家为同一个玩家时，【运气王】显示的玩家顺延至胜率排名第二的玩家
    if luck_uid == ordered_players[hhdz.RICH_SEAT_SIDE] then
        if #list < 2 then
            return
        end
        uid_luck_data = list[2]
        luck_uid = uid_luck_data.uid
    end
    --运气王的下注金额必须达到上一局玩家的下注金额均值
    if uid_luck_data.total_bet < player_avg_bet then
        for k, uid_info in pairs(list) do
            if uid_info.total_bet >= player_avg_bet and uid_info.uid ~= ordered_players[hhdz.RICH_SEAT_SIDE] then
                luck_uid = uid_info.uid
                break
            end
        end
    end

    deal_special_pos(luck_uid,hhdz.LUCK_SEAT_SIDE)
end

local function update_player_special_data()
    for uid,player_info in pairs(player_info_list) do
        player_info.win_count = 0
        player_info.total_bet = 0
        player_info.play_count = 0

    end
    for _,special_list in pairs(record_special_list) do
        for _uid,uid_special in pairs(special_list) do
            local player_info = player_info_list[_uid]
            if player_info then
                --更新一下玩家身上的数据
                player_info.win_count = (player_info.win_count or 0) + (uid_special.is_win or 0)
                player_info.total_bet = (player_info.total_bet or 0) + (uid_special.bet_coins or 0)
                player_info.play_count = (player_info.play_count or 0) + (uid_special.is_bet or 0)
            end
        end
    end
    special_req_sit()
end

--更新富豪No.1和运气王位置玩家
local function update_special_sit(win_list)
    if not curr_hhdz_instance then 
        print("=============nilnilnilnil")
        return 
    end

    record_special_list[#record_special_list+1] = {} 
    if #record_special_list > 20 then
        table_remove(record_special_list,1)
    end

    local uid_special_list = record_special_list[#record_special_list]
    local player_total_bet = 0 --本局玩家总下注
    local bet_player_num = 0   --本局下注的玩家

    local player_bet_coins = curr_hhdz_instance:get_player_bet_coins()
    for uid, bet_coins in pairs(player_bet_coins) do
        uid_special_list[uid] = {}
        local uid_specil = uid_special_list[uid]
        uid_specil.bet_coins = bet_coins
        if win_list[uid] and win_list[uid] > 0 then
            uid_specil.is_win = 1
        else
            uid_specil.is_win = 0
        end
        uid_specil.is_bet = 0     --玩家是否有下注行为，用于统计桌子近20局玩家下注总局数
        --玩家的下注总金额
        if bet_coins > 0 then
            player_total_bet = player_total_bet + bet_coins
            bet_player_num = bet_player_num + 1
            uid_specil.is_bet = 1
        end
    end
    --记录一下该局玩家的下注金额均值
    player_avg_bet = 0
    if bet_player_num > 0 then
        player_avg_bet = math_floor(player_total_bet / bet_player_num)
    end
    print("===========aaaaaaaaaa",player_avg_bet)
end

local function audit_game_result(game_result)
    local position_map = {}
    for pos,uid in pairs(ordered_players) do
        if uid ~= EMPTY_SEAT then
            position_map[uid] = pos
        end
    end
    
    --发钱
    local betting_result_list = {}
    local tmp_betting_result_map = {}
    local player_side_map = game_result.player_side_map or {}
    for uid,coins in pairs(game_result.compensation_map) do
        local betting_result = {uid = uid,coins = coins}
        tmp_betting_result_map[uid] = betting_result
        table_insert(betting_result_list,betting_result)
        local player_info = player_info_list[uid]
        if player_info then
            betting_result.curr_coins = player_info.coins
            betting_result.side_result = player_side_map[uid] or {}
        end

        if not is_robot(uid) then
            print("gggggggggggggggggggggggg",uid,coins)
            --添加输赢次数
            if coins >= 0 then
                R().basesvr({key=uid}):send('.msg_handler','add_wining_times',uid,self_game_type)
                            --检查是否触发财神驾到
                if player_info  then
                    local vip_level = player_info.vip_level
                    print("===========",uid,coins,self_table_type,vip_level or 0)
                    if system_store.check_trigger_caishenjiadao(uid,coins,self_table_type,vip_level or 0) then
                        
                        notify_caishenjiadao(uid,coins)
                    end
                end
                --添加玩家首冲进度
                -- R().activitysvr(1):send('.activity_mgr','act_add_first_charge_process',uid,coins + (game_result.pay_fee_map[uid] or 0))
            elseif coins < 0 then
                R().basesvr({key=uid}):send('.msg_handler','add_losing_times',uid,self_game_type)
            end
        end
    end

    local give_winner_coins_funcs = {}
    local roomdata = global_configs.roomdata[self_table_type]
    for uid,coins in pairs(game_result.winner_coin_map) do
        if coins > 0 then
            local betting_result = tmp_betting_result_map[uid]
            if is_robot(uid) then
                local player_info = player_info_list[uid]
                player_info.coins = player_info.coins + coins
                betting_result.curr_coins = player_info.coins
                -- R().basesvr({key=uid}):send('.msg_handler','add_player_fee_coins',uid,game_result.pay_fee_map[uid] or 0)
                -- billlog({op="reducecoins", uid = uid,curr = player_info.coins,value = game_result.pay_fee_map[uid],is_robot = is_robot(uid),r = reason.PAY_FEE}, get_extra_bill_info(uid))
            else
                table_insert(give_winner_coins_funcs,{
                    f = give_winner_coins,
                    id = uid,
                    params = {uid,coins,betting_result, game_result.pay_fee_map}
                })
            end
        end
    end

    local rsp_game_result = {
        winner = game_result.winner_side,
        type = game_result.compensation_type,
        red_type = game_result.red_type,
        red_card_list = game_result.red_card_list,
        black_type = game_result.black_type,
        black_card_list = game_result.black_card_list,
        betting_result_list = betting_result_list,
    }

    g_game_result_info = rsp_game_result
    if #g_history_list >= 72 then
        g_history_list = {}
        ----删除末尾
        --table_remove(g_history_list)
    end
    table_insert(g_history_list,1,{
        side = game_result.winner_side,
        type = game_result.winner_type,
        compensation_type = game_result.compensation_type,
    })

    local ok,results = cocall(5,give_winner_coins_funcs)
    if not ok then
        errlog('failed to cocall',tostring_r(results))
    end
    local curr_time = util.get_now_time()
    notify_all('honghei.NTF_GAMEOVER',{
        game_result = g_game_result_info,
        left_time = CLIENT_GAME_STATUS_GAMEOVER_TIME
    })

    --这里先通知每个人自身金币发生变化
    local money_changed_reason = constant.MONEY_CHANGED_REASON.HHDZ_AUDIT
    for uid,ret in pairs(results) do
        if ret then
            --这时候不确保玩家是否一定在桌子上，因此需要走大厅去通知金币变化
            if not is_robot(uid) then
                R().hallsvr{key=uid}:send('.msg_handler','toagent',uid,
                    'notify_money_changed',{coins = ret},money_changed_reason)
            end
            local player_info = player_info_list[uid]
            if player_info then
                player_info.coins = ret
            end
        else
            errlog(uid,'failed to cocall...',ret)
        end
    end

    sub_lose_coins(game_result.winner_coin_map)
    --add_lose_coins(game_result.compensation_map)
    
    --计算系统输赢
    local side_winner_coin  = 0
    local system_money_cost = 0
    local total_fee_coins = 0
    for uid,coins in pairs(game_result.winner_coin_map) do
        if coins > 0 and not is_robot(uid) then
            side_winner_coin = side_winner_coin + coins
        end
    end

    for uid,coins in pairs(game_result.pay_fee_map) do
        if not is_robot(uid) then
            total_fee_coins = total_fee_coins + coins
        end
    end
    side_winner_coin = side_winner_coin + total_fee_coins

    local betting_list_no_robot = curr_hhdz_instance:get_betting_list_no_robot()
    for _, coin in pairs(betting_list_no_robot) do
        system_money_cost = system_money_cost + coin
    end
    last_betting_coins = system_money_cost

    print("betting_list_no_robot==========before",system_money_cost,side_winner_coin,total_fee_coins,tostring_r(betting_list_no_robot))
    system_money_cost = system_money_cost - side_winner_coin
    --print("system_money_cost==1111",system_money_cost)

    --玩家影响额度
    --print("==========control_uid",control_uid)
    gm_store_coins_add = 0
    if control_uid then
        local my_betting_list = curr_hhdz_instance:get_my_betting_list(control_uid)
        local bet_coins = 0
        for side,coins in pairs(my_betting_list) do
            bet_coins = bet_coins + coins
        end
        local win_coins = game_result.winner_coin_map[control_uid] or 0
        local pay_coins = game_result.pay_fee_map[control_uid] or 0

        local control_coins = math_abs(win_coins + pay_coins) - bet_coins
        if control_coins > 0 and control_result == CONTROL_RESULT_WIN then
            local ok,coins = R().basesvr({key=control_uid}):call('.msg_handler','reduce_curr_control_data',control_uid,control_coins,self_table_type,true)
            local will_cost_coins = (ok and coins) and coins or 0
            local gm_add_coins = math_abs(system_money_cost) < math_abs(will_cost_coins) and math_abs(system_money_cost) or math_abs(will_cost_coins)
            gm_store_coins_add = -gm_add_coins
        elseif control_coins < 0 and control_result == CONTROL_RESULT_LOSE then
            local ok,coins = R().basesvr({key=control_uid}):call('.msg_handler','add_curr_control_data',control_uid,math_abs(control_coins),self_table_type,true)
            local will_cost_coins = (ok and coins) and coins or 0
            local gm_add_coins = math_abs(system_money_cost) < math_abs(will_cost_coins) and math_abs(system_money_cost) or math_abs(will_cost_coins)
            gm_store_coins_add = gm_add_coins
        end
    end

    print("system_money_cost==222",system_money_cost,gm_store_coins_add,system_money_cost - gm_store_coins_add,control_uid)
    system_money_cost = system_money_cost - gm_store_coins_add --去掉GM库存部分

    local roomdata = global_configs.roomdata[self_table_type] or {}
    local robot_type = roomdata.robot_type or "hhdz_normal"
    local is_update_reward_store = true
    if system_money_cost >= 0 then --玩家输 加系统库存
        R().exdbsvr(1):send('.system_coins_mgr','add_system_coins',self_game_type,self_table_type,system_money_cost)
    else
        if system_send_award then--从奖励库存里面扣钱
            R().exdbsvr(1):send('.system_coins_mgr','reduce_award_coins',self_game_type,self_table_type,math_abs(system_money_cost))
            is_update_reward_store = false
        else--从系统库存里面扣钱
            R().exdbsvr(1):send('.system_coins_mgr','sub_system_coins',self_game_type,self_table_type,math_abs(system_money_cost))
        end
    end
    --增加玩家今天流水
    add_player_coins_water(game_result.compensation_map, game_result.pay_fee_map)

    local total_coins,win_list = write_card_record_log(game_result)
    print("total_coins",total_coins)
    if is_update_reward_store then --从系统库存中分一部分去抽水库存和奖励库存
        total_coins = total_coins - math_abs(gm_store_coins_add) --去掉GM库存部分
        update_robot_store(total_coins)
    end
    check_store_transfer() --检测库存转移

    send_real_horse_message(win_list)
    update_special_sit(win_list)

    return true
end

local function have_player()
    for uid,_ in pairs(player_info_list) do
        if not is_robot(uid) then
            return true
        end
    end
    return false
end

local function unlock_all_players()
    for uid,_ in pairs(table_players) do
        unlock_one_player(uid,this_table_gid)
    end
end

local function put_back_robots()
    local robot_ids_list = {}
    for uid, _ in pairs(player_info_list) do
        if is_robot(uid) and robot_manager[uid] then
            robot_manager[uid]['on_exit'](robot_manager[uid])
            -- if enter_robot_map[uid] then
            --     enter_robot_map[uid] = nil 
            -- end
        end
    end
end

local function notify_matchsvr_table_stats(is_delete)
    local table_stats = {}
    if is_delete then
        table_stats.table_gid = this_table_gid
        table_stats.table_type = self_table_type
        R().newmatchsvr(1):send('.table_mgr','delete_table_stats',table_stats)
    else
        table_stats.table_type = self_table_type
        table_stats.left_num = table_def.game_player_num[self_game_type] - get_player_num()
        table_stats.table_gid = this_table_gid
        table_stats.table_pattern = self_table_pattern
--        dbglog(tostring_r(table_stats))
        R().newmatchsvr(1):send('.table_mgr','update_table_stats',table_stats)
    end
end

------------------------------------机器人----------------------------

local function robot_num()
    --如果位置已经坐了机器人了那么不再去拉取机器人了
    local robot_num = 0
    for uid in pairs(table_players) do
        if is_robot(uid) then
            robot_num = robot_num + 1
        end
    end
    return robot_num
end

--加载机器人
local function load_robot()
    local roomdata = global_configs.roomdata[self_table_type]
    if not roomdata or not roomdata.open_robot then
        errlog('load_robot fail because roomdata is nil or not open_robot')
        return
    end
    local robot_type = roomdata.robot_type or -1
    local robot_control  = global_configs.robot[robot_type]
    if not robot_control then
        errlog('load_robot fail because robotdata is nil')
        return
    end

    --if not next(player_info_list) then --如果没有玩家那么不去加载机器人
    --    return
    --end
    local robot_type_list = robot_control.robot_type_list

    --获取现有机器人人数
    local curr_robot_num_map = {}
    for uid,player_info in pairs(player_info_list) do
        local robot_type = player_info.coins_type
        if robot_type then
            curr_robot_num_map[robot_type] = (curr_robot_num_map[robot_type] or 0) + 1
        end
    end

    local load_list = {}
    for type,data in pairs(robot_type_list) do
        local robot_num = utils.str_split(data.count_range,',')
        local coins_limit = utils.str_split(data.coins_range,',')
        local count = math_random(tonumber(robot_num[1]) or 0,tonumber(robot_num[2]) or 0)
        print("count",count,curr_robot_num_map[type])
        local curr_count = curr_robot_num_map[type] or 0
        if count > curr_count then
            load_list[type] = {count = count - curr_count,
                                coins_limit = {tonumber(coins_limit[1]) or 0,tonumber(coins_limit[2]) or 0}}
        end
    end

    

    print_r(load_list)
    local ok,succ,ret_map = R().robotmgr(1):call(".msg_handler","get_robot_type_list",load_list,robot_type)
    if not ok or not succ or not ret_map then
        errlog("load rebot is fail because succ is nil")
        return
    end

    if get_player_num() >= table_def.game_player_num[self_game_type] then
        R().robotmgr(1):send('.msg_handler','put_back_robots',ret_map)
        return
    end

    local curr_time = util.get_now_time()
    local new_robot_list = {}
    if next(ret_map) then
        robot_enter_list = {}
    end
    for _, robot_info in pairs(ret_map) do
        local robot_uid = robot_info.uid
        local player_info = {
            uid = robot_uid,
            name = robot_info.name,
            coins = robot_info.coins,
            sex = robot_info.sex,
            icon = robot_info.icon,
            coins_type = robot_info.coins_type,
            last_chat_time = 0,
            last_ping_time = curr_time,
            icon_border = robot_info.icon_border,
            win_count = 0,
            total_bet = 0,
            play_count = 0,
            vip_level = robot_info.vip_level,
        }

        local betting_list = {100,1000,5000,10000,50000}
        local available_betting_list = get_betting_list(global_configs.bet_list.hhdz_bet_list,player_info.coins) or betting_list
        player_info.available_betting_list = available_betting_list
--        print_r(player_info)
        assert(not table_players[robot_uid],robot_uid)
        assert(not player_info_list[robot_uid],robot_uid)
        table_players[robot_uid] = ROBOT_CLIENT_FD
        player_info_list[robot_uid] = player_info
        add_to_noseat(robot_uid)
        
        local robot_obj = game_robot.new(robot_uid,player_info,robot_type,skynet.self(),ROBOT_CLIENT_FD)
        robot_manager[robot_uid] = robot_obj
        robot_obj:canBet()
        --enter_robot_map[robot_uid] = true
        table_insert(new_robot_list,robot_obj)
        trigger_event('on_register',robot_uid)
        --机器人VIP入场广播
        table_insert(robot_enter_list, robot_info.uid)
        dbglog(robot_uid,"add robot obj")
    end

    run_robots(new_robot_list) --fork robot协程
    notify_matchsvr_table_stats() --通知匹配服更新人数
end

local function get_non_robot_players_num()
    local player_num = 0

    for _,uid in pairs(ordered_players) do
        if not is_robot(uid) then
            player_num = player_num + 1
        end        
    end

    return player_num    
end

local function have_player_betting()
    local betting_uid_list = curr_hhdz_instance:get_betting_uid_list()
    for _,uid in pairs(betting_uid_list) do
        if not is_robot(uid) then
            return true
        end
    end
    return false
end

local function get_lose_coins(uid)
    local ok,ret = R().basesvr{key=uid}:call('.msg_handler','get_lose_coins',uid)
    if not ok then
        errlog(uid,'failed to get_lose_coins ok is nil',ok)
        return
    end

    return ret
end

local function leave(uid)
    local game_session = table_players[uid]
    --取消跟网关的绑定关系
    if game_session ~= OFFLINE_CLIENT_FD then
        watch_session(game_session,uid,false)
    end

    --如果玩家有下注那么直接设置玩家的session为离线
    if uid_betting_map[uid] then
        --有押注过,暂时还不能解锁
        dbglog(uid,'this player is locked now...',table_players[uid])
        table_players[uid] = OFFLINE_CLIENT_FD
        return false
    end

    if not robot_manager[uid] then
        skynet.send('.table_mgr','lua','leave',uid)
        --R().hallsvr({key=uid}):send('.msg_handler', 'check_lucky_tigger',uid,self_game_type,init_coins) 注释掉鸿运系统
    end

    clear_and_unlock_offline_player(uid)

    notify_matchsvr_table_stats()
    if not is_robot(uid) then
        R().friendsvr(1):send('.info_mgr','update_info',uid,{curr_game_type=0})
    end
    --归还机器人
    if robot_manager[uid] then
        robot_manager[uid]['on_exit'](robot_manager[uid])
        robot_manager[uid] = nil
    end

    -- if enter_robot_map[uid] then
    --     enter_robot_map[uid] = nil 
    -- end

    return true
end

local function clear_offline_players()
    for uid,status in pairs(table_players) do
        if status == OFFLINE_CLIENT_FD then
            local ok,ret = xpcall(leave,debug.traceback,uid)
            if not ok then
                errlog(uid,ret)
            end
        end
    end
end

local function dissmiss_table()
    local uids = {}
    local robots = {}
    for uid,player_info in pairs(player_info_list) do
        local game_session = table_players[uid]
        local robot = is_robot(uid)
        leave(uid)
        if not robot then
            send_to_gateway(uid,game_session,'honghei.RSP_LEAVE',{result=error_code.RSP_SUCESS})
            --watch_session(game_session,uid,false)
        end
        
        --skynet.send('.table_mgr','lua','leave',uid)

        local roomdata = global_configs.roomdata[self_table_type]
        if robot and roomdata and roomdata.robot_type then
            table_insert(robots,{type=roomdata.robot_type, uid=uid, coins=player_info.coins})
        end
            
        table_insert(uids,uid)
    end

    notify_matchsvr_table_stats(true)  --更新桌子
    skynet.send('.table_mgr','lua','on_table_delete',self_table_id,uids)    --删除状态
    R().robotmgr(1):send('.msg_handler','put_back_robots',robots) --归还机器人
    return true

end

local function have_player()
    for uid,_ in pairs(player_info_list) do
        if not is_robot(uid) then
            return true
        end
    end
    return false
end

local function  close_table()
    local ok,ret = xpcall(dissmiss_table,debug.traceback)
    if not ok then
        errlog(ret)
    end

    billlog({op = "close_table",status = curr_status,password = self_password})
    dbglog('now delete this table',self_table_id)
    skynet.exit()
end

local function check_stop_table()
    local roomdata = global_configs.roomdata[self_table_type] or {}
    if stopping_server or roomdata.open_game == false then         
        billlog({op = "stop_table",status = curr_status,password = self_password})
        closing_server = true --直接关闭桌子
        close_table()
        return true
    end

    return false
end

local function check_bet_num()
    local ret5,ret4 = curr_hhdz_instance:check_bet_num(player_info_list,player_not_bet_num)
    local roomdata = global_configs.roomdata[self_table_type] or {}
    for _,uid in pairs(ret5) do
        local robot = is_robot(uid)  
        if not robot then
            dbglog("RSP_KICK_LEAVE ",uid)
            send_to_gateway(uid,table_players[uid],'table.NTF_LEAVE',{uid = uid ,reason=4,room_name = roomdata.name})
        end
        leave(uid)
    end
    for _,uid in pairs(ret4) do
        local robot = is_robot(uid)  
        if not robot then
            send_to_gateway(uid,table_players[uid],'table.NTF_TIPS',{type = 0,content = constant.BR_NO_BET_TIP})
        end
    end
end

local function check_close_table(curr_time)
    local closable = false
    if closing_server then
        closable = true
    end
    
    if not closable then
        return
    end
    close_table()
end

local function get_side_card_type(mix_index)
    local side = mix_index >> 16
    local card_type = mix_index & 15
    --print("=========",side,card_type)
    return side,card_type
end

--获取牌型概率值
local function get_card_power(card_type)
    local card_range = hhdz.CARD_TYPE_PROBABILITY[card_type]
    return card_range[2] - card_range[1]
end

local function special_random(spceial_card_type_list)
    print_r(spceial_card_type_list)
    local card_type_map = {}
    local side_list_map = {}
    local total_power = 0
    for _,mix_index in pairs(spceial_card_type_list) do
        local side,card_type = get_side_card_type(mix_index)
        local card_power = get_card_power(card_type)
        card_type_map[mix_index] = card_power
        total_power = total_power + card_power
    end
    local rand_num = math_random(1,total_power)
    
    local tmp_num = 0
    for mix_index,power in pairs(card_type_map) do
        tmp_num = tmp_num + power
        if tmp_num >= rand_num then
            return mix_index
        end
    end

    return spceial_card_type_list[math_floor(1,#spceial_card_type_list)]
end

local function get_random_side(rand_list,special_rate)
    if #rand_list == 1 then
        return rand_list[1].side
    end

    local spceial_list = {}
    local normal_list = {}
    local all_list = {}
    for k,v in pairs(rand_list) do
        local side,card_type = get_side_card_type(v.side)
        if card_type ~= 0 then
            table_insert(spceial_list,v.side)
        else
            table_insert(normal_list,v.side)
        end
        table_insert(all_list,v.side)
    end
    print("get_random_side",tostring_r(spceial_list),tostring_r(normal_list))
    local rand = math_random(1,100)
    --如果随机到特殊牌型则随机出一种特殊牌型，反之随机一种高牌，没有高牌则从特殊牌型中取一种
    if rand <= special_rate and #spceial_list > 0 then
        return special_random(spceial_list),0
    elseif #normal_list > 0 then
        local ret = 0
        if #normal_list == 1 then
            ret = 1
        end
        local index = math_random(1,#normal_list)
        return normal_list[index],ret
    elseif #spceial_list > 0 then
       -- print("kkkkkkkkkkkkkkkkk")
        return special_random(spceial_list),0
    end

    return special_random(all_list),0
end

local function gen_side_result_list(betting_list,system_win_list_side,system_lose_list_side,random_list_side,store_result)
    local roomdata = global_configs.roomdata[self_table_type] or {}
    local robot_type = roomdata.robot_type or "hhdz_normal"
    local robot_store = global_configs.robot_store
    local robot_config = robot_store[self_table_type]
    local lose_limit = robot_config.base_lose_limit or 100000
    if (store_result or 0) == constant.AWARD_STORE_LOSE then
        lose_limit = robot_config.award_lose_limit or 100000
    end
    print("gen_side_result_list=====",store_result,lose_limit)

    local bet_coins = 0
    for _,coins in pairs(betting_list) do
        bet_coins = coins + bet_coins
    end

    --所有玩家下注列表,玩家GM控制的时候需要计算系统输赢是否超出限制
    local player_betting_list = curr_hhdz_instance:get_betting_list_no_robot()
    local all_player_bet_coins = 0
    for _,coins in pairs(player_betting_list) do
        all_player_bet_coins = all_player_bet_coins + coins
    end

    --开特殊牌型时，不能开超出限制的特殊类型
    local red_special_list = {}
    local black_special_list = {}
    
    --某一边赢并且开出的牌型对应的输赢
    --index = red << 16 | card_type
    local award_list = {}
    for i = 1,2 do
        local coins = betting_list[i] or 0
        for card_type,_ in pairs(hhdz.CARD_TYPE_PROBABILITY) do
            local index = i << 16 | card_type
            local award_coins = coins * 2
            local all_player_award_coins = (player_betting_list[i] or 0) * 2
            if hhdz.COMPENSATION_RATE[card_type] then
                --如果是特殊牌型，需要计算特殊牌型赔率
                award_coins = award_coins + (betting_list[3] or 0) * hhdz.COMPENSATION_RATE[card_type]
                all_player_award_coins = all_player_award_coins + (player_betting_list[3] or 0) * hhdz.COMPENSATION_RATE[card_type]
            end
            local player_win_coins = award_coins - bet_coins
            if all_player_award_coins - all_player_bet_coins <= lose_limit  then
                award_list[index] = award_coins - bet_coins
            end
        end
    end

    for side,coins in pairs(award_list) do
        if coins < 0 then
            table_insert(system_win_list_side,{side = side,coins = coins})
        else
            table_insert(system_lose_list_side,{side = side,coins = coins}) 
        end
        table_insert(random_list_side,{side = side,coins = coins})
    end

    return
end

local function control_player_result(uid,win,store_result)
    local betting_list = curr_hhdz_instance:get_my_betting_list(uid)
    if not betting_list or not next(betting_list) then
        return
    end

    --开不同边的输赢列表
    local system_win_list_side = {}
    local system_lose_list_side = {}
    local random_list_side = {}

    gen_side_result_list(betting_list,system_win_list_side,system_lose_list_side,random_list_side,store_result)
    print("=============result_list",win,tostring_r(system_win_list_side),tostring_r(system_lose_list_side),tostring_r(random_list_side))
    --随机出一种结果
    local roomdata = global_configs.roomdata[self_table_type] or {}
    local robot_type = roomdata.robot_type or "hhdz_normal"
    local robot_store = global_configs.robot_store
    local robot_config = robot_store[self_table_type]
    local special_rate = robot_config.special_rate or 0
    if win and #system_lose_list_side > 0 then
        --玩家赢需要从系统输的结果里随机一种
        return get_random_side(system_lose_list_side,special_rate)
    elseif not win and #system_win_list_side > 0 then
        return get_random_side(system_win_list_side,special_rate)
    end

    return
end

local function check_has_player_betting(betting_list)
    for side,coins in pairs(betting_list) do
        if coins > 0 then
            return true
        end
    end

    return false
end

local function get_system_index(win_type,store_result)       --1、系统赢 2、系统输 0、随机
    local betting_list = curr_hhdz_instance:get_betting_list_no_robot()
    dbglog("hhdz_get_system_index==11",win_type,tostring_r(betting_list))
    if not check_has_player_betting(betting_list) then
        print("hhdz_no_player=====")
        win_type = constant.SYSTEM_SIDE_RANDOM
    end

    --开不同边的输赢列表
    local system_win_list_side = {}
    local system_lose_list_side = {}
    local random_list_side = {}

    --開某一边系统对应的输赢金额
    gen_side_result_list(betting_list,system_win_list_side,system_lose_list_side,random_list_side,store_result)
    print("=============result_list",win_type,tostring_r(system_win_list_side),tostring_r(system_lose_list_side),tostring_r(random_list_side))
    
    local roomdata = global_configs.roomdata[self_table_type] or {}
    local robot_type = roomdata.robot_type or "hhdz_normal"
    local robot_store = global_configs.robot_store
    local robot_config = robot_store[self_table_type]
    local special_rate = robot_config.special_rate or 0
    if win_type == constant.SYSTEM_SIDE_WIN and #system_win_list_side > 0 then
        return get_random_side(system_win_list_side,special_rate)
    elseif win_type == constant.SYSTEM_SIDE_LOSE and #system_lose_list_side > 0 then
        return get_random_side(system_lose_list_side,special_rate)
    elseif win_type == constant.SYSTEM_SIDE_RANDOM then
        return get_random_side(random_list_side,special_rate)
    end

end


local function check_have_robot(player_list)
    for _,uid in pairs(player_list) do
        if is_robot(uid) then
            return true
        end
    end

    return false
end

local function get_personal_system_store_result(robot_type)
    local player_list = {}
    local betting_list = curr_hhdz_instance:get_betting_uid_list()
    for _,uid in pairs(betting_list) do
        if not is_robot(uid) then
            table_insert(player_list,uid)
        end
    end
    local robot_store = global_configs.robot_store[self_table_type]     
    local personal_control = global_configs.personal_control
    local is_have_robot = check_have_robot(betting_list)
    is_curr_have_robot = is_have_robot

    local gm_warn = (global_configs.gm_control or {}).warn_coins or 50000000
    --dbglog("===========get_personal_system_store_result=========",robot_type)
    local base_warn = robot_store.base_min or 0 
    return system_store.get_personal_system_store_result(self_table_type,player_list,gm_warn,is_have_robot,base_warn)
end

local function get_store_result_index()
    local index
    system_send_award = false 
    local roomdata = global_configs.roomdata[self_table_type] or {}
    local robot_type = roomdata.robot_type or "hhdz_normal"

    local store_result,personal_uid = get_personal_system_store_result(robot_type)
    system_result_type = store_result
    err_result = 0
    gm_control_uid = personal_uid
    if not store_result then
        errlog("failed to get store_result",store_result,personal_uid)
        return
    end

    --dbglog("hhdz_get_store_result_index==begin===",store_result,personal_uid)
    control_uid = personal_uid
    if store_result == constant.SYSTEM_STORE_WIN then
        index = get_system_index(constant.SYSTEM_SIDE_WIN,store_result)
    elseif store_result == constant.SYSTEM_STORE_LOSE then
        index = get_system_index(constant.SYSTEM_SIDE_LOSE,store_result)
    elseif store_result == constant.AWARD_STORE_LOSE then
        index = get_system_index(constant.SYSTEM_SIDE_LOSE,store_result) --让系统输
        system_send_award = true
    elseif store_result == constant.PERSONAL_STORE_WIN then   
        control_result = CONTROL_RESULT_WIN
        index = control_player_result(personal_uid,true,store_result)
    elseif store_result == constant.PERSONAL_STORE_LOSE then
        control_result = CONTROL_RESULT_LOSE
        index = control_player_result(personal_uid,false,store_result)
    end

    if not index then
        if store_result ~= constant.SYSTEM_NO_CONTROL then
            err_result = 1
        end
        index,err_result = get_system_index(constant.SYSTEM_SIDE_RANDOM,store_result)
        dbglog("get_random_side=====",index)
    end
    dbglog("hhdz_get_store_result_index==end==store_result=",store_result,"index=",index,"personal_uid=",personal_uid)

    return index
end

local function check_robot_coins()
    local roomdata = global_configs.roomdata[self_table_type] or {}
    local robot_type = roomdata.robot_type or "hhdz_normal"
    local robot_control = global_configs.robot
    for uid,player_info in pairs(player_info_list) do
        if is_robot(uid) then --如果是机器人，检查携带金币数高于该类型上限或者低于下限          
            local coins_type = player_info.coins_type
            if not coins_type then goto continue end
            local robot_config = robot_control[robot_type]
            local robot_type_info = robot_config.robot_type_list[coins_type]
--            print_r(robot_type_info)
            local range = utils.str_split(robot_type_info.coins_range,',')
            if #range ~= 2 then
                return
            end

            if player_info.coins < tonumber(range[1]) or player_info.coins > tonumber(range[2]) then
                leave(uid)
            end
        end
        ::continue::
    end
end

local function record_player_start_coins()
    for uid,player_info in pairs(player_info_list) do
        start_coins_map[uid] = player_info.coins
    end
end

local function set_curr_special_uid()
    if not ordered_players[hhdz.RICH_SEAT_SIDE] or not ordered_players[hhdz.LUCK_SEAT_SIDE] then 
        special_req_sit()
    end
end

--获取每种类型的机器人可下注总额度
local function get_robot_total_bet_coins()
    local roomdata = global_configs.roomdata[self_table_type] or {}
    local robot_type = roomdata.robot_type or -1
    local robot_control  = global_configs.robot[robot_type]
    if not robot_control or not robot_control.robot_type_list then
        return
    end
    
    local robot_bet_coins = {}
    for coins_type, v in pairs(robot_control.robot_type_list) do
        local last_bet_rate = v.system_last_bet_rate / 100  --上局下注选用比例
        local bet_min = v.system_bet_min +  (last_player_betting_coins or 0) * last_bet_rate   --机器人下注下限
        local bet_float = utils.str_split(v['bet_area_float'],'-')
        local float = math_random(tonumber(bet_float[1]),tonumber(bet_float[2]))

        --可下注额度
        local total_bet = math_floor(bet_min * (float / 100))
        robot_bet_coins[coins_type] = total_bet
    end
    return robot_bet_coins
end

--指定类型的机器人能下的总注
local function get_robot_total_coins()
    local robot_total_coins_type = {}
    for uid,v in pairs(player_info_list) do
        if is_robot(uid) then --剔除上庄机器人的钱
            local robot = robot_manager[uid]
            local coins = v.coins
            if robot and not robot:getCanBet() then 
                coins = 0
            end            
            if robot_total_coins_type[v.coins_type] then
                robot_total_coins_type[v.coins_type] = robot_total_coins_type[v.coins_type] + (coins or 0)
            else
                robot_total_coins_type[v.coins_type] = (coins or 0)
            end
        end
    end
    return robot_total_coins_type
end

-------------------------------游戏主循环------------------------
function update(curr_time)
    --print('=============curr status',tostring(curr_status))
    if curr_status == TABLE_STATUS_WAITTING then
        if curr_time - game_start_wait_time >= GAME_STATUS_WAIT_TIME then
            curr_status = TABLE_STATUS_CHECK_START
            load_robot()
            dbglog("TABLE_STATUS_WAITTING curr_time:",curr_time,TABLE_STATUS_WAITTING)
        end
    elseif curr_status == TABLE_STATUS_CHECK_START then
        if check_start(curr_time) then
            game_start_real_time_pad = curr_time
            --game_start_betting_time = curr_time
            curr_status = TABLE_STATUS_REAL_TIME_PAD_BEGIN
            curr_round = curr_round + 1
            g_betting_batch_list = {} 
            notify_all('honghei.NTF_START_PAD',{
                left_time = GAME_REAL_TIME_PAD
            })
            dbglog("TABLE_STATUS_CHECK_START curr_time:",curr_time,TABLE_STATUS_CHECK_START)
        end
    elseif curr_status == TABLE_STATUS_REAL_TIME_PAD_BEGIN then --开始动画
        if curr_time - game_start_real_time_pad >= GAME_REAL_TIME_PAD then
            game_start_betting_time = curr_time
            curr_status = TABLE_STATUS_BETTING
            local robot_total_coins_type = get_robot_total_coins()
            local robot_bet_coins = get_robot_total_bet_coins()
            curr_hhdz_instance:start_betting()
            trigger_event('on_start',curr_hhdz_instance,robot_bet_coins,robot_total_coins_type)
          
            notify_all('honghei.NTF_START',{
                left_time = CLIENT_GAME_STATUS_BETTING_TIME
            })
            record_player_start_coins() --记录游戏开始玩家金币
             dbglog("TABLE_STATUS_REAL_TIME_PAD_BEGIN curr_time:",curr_time,TABLE_STATUS_REAL_TIME_PAD_BEGIN)
        end
    elseif curr_status == TABLE_STATUS_BETTING then
        if curr_time - game_start_betting_time >= CLIENT_GAME_STATUS_BETTING_TIME then
            game_start_game_over_time = curr_time
            curr_status = TABLE_STATUS_GAMEOVER
           dbglog("TABLE_STATUS_BETTING curr_time:",curr_time,TABLE_STATUS_BETTING)
        end
    elseif curr_status == TABLE_STATUS_GAMEOVER then
        if curr_time - game_start_game_over_time >= GAME_REAL_TIME_PAD_END then     
            curr_status = TABLE_STATUS_GAMEOVER_WAIT
            game_start_game_over_wait_time = curr_time

            local roomdata = global_configs.roomdata[self_table_type]
            local fee_percent = (roomdata.cost or 5)/ 100 

            local affect_index = get_store_result_index()
            curr_hhdz_instance:judge_result(fee_percent,affect_index)
            local ok,ret = xpcall(audit_game_result,debug.traceback,
                curr_hhdz_instance:get_game_result())
            if not ok then
                errlog(ret)
            end
            dbglog("TABLE_STATUS_GAMEOVER curr_time:",curr_time,TABLE_STATUS_GAMEOVER)
            uid_betting_map = {}
            clear_offline_players()
        end
        --trigger_event('on_game_over')
    elseif curr_status == TABLE_STATUS_GAMEOVER_WAIT then
        if curr_time - game_start_game_over_wait_time >= GAME_STATUS_GAMEOVER_TIME then
         --   dbglog("TABLE_STATUS_GAMEOVER_WAIT curr_time:",curr_time,TABLE_STATUS_GAMEOVER_WAIT)
            load_config()
            trigger_event('on_game_over')
            self_game_config = global_configs.roomdata[self_table_type]
            set_sit_down()           --已入座玩家可能因为金币不足要被弹起
            check_bet_num()
            --check_robot_coins()
            curr_status = TABLE_STATUS_RESTART
            check_stop_table()
            --更新富豪No.1和运气王
            update_player_special_data()
        end
    elseif curr_status == TABLE_STATUS_RESTART then
      --  dbglog("TABLE_STATUS_RESTART curr_time:",curr_time,TABLE_STATUS_RESTART)
        curr_hhdz_instance = nil
        g_game_result_info = nil
        curr_status = TABLE_STATUS_WAITTING
        game_start_wait_time = curr_time    
        notify_all('honghei.NTF_WAIT_START',{left_time = CLIENT_GAME_STATUS_WAIT_TIME})
        -- load_config()
    else
        errlog('unknown status...',curr_status)
    end

    set_curr_special_uid()
    check_robot_ntf_enter(curr_time)
    check_close_table(curr_time)
end

local function game_update()
    curr_status = TABLE_STATUS_WAITTING
    --进来先加载一次机器人
    load_robot()
    while true do
        local curr_time = util.get_now_time()
        local ok,ret = xpcall(update,debug.traceback,curr_time)
        if not ok then 
            errlog(ret)
        end
        skynet.sleep(50)
    end
end

function internal.get_player_num()
    return get_player_num()
end

function internal.sync_table_stats()
    local left_num = table_def.game_player_num[self_game_type] - get_player_num()
    if left_num < 0 then left_num = 0 end

    local table_stats = {}
    table_stats.table_gid = this_table_gid
    table_stats.left_num = left_num
    table_stats.table_pattern = self_table_pattern
    return self_table_type,table_stats
end

function internal.get_game_type()
    return self_game_type,self_table_type
end

function internal.get_user_playing_info()
    local playing_info = {}
    playing_info.game_type = self_game_type
    playing_info.table_type = self_table_type
    playing_info.total = get_non_robot_players_num()

    return playing_info
end

local function get_bet_index(available_bet_list,bet_coins)
    for index,coins in pairs(available_bet_list) do
        if coins == bet_coins then
            return index
        end
    end
end

---------------------------客户端请求类处理----------------------------------------
local function collect_betting_action(uid,player_info,side,betting_coins)
    local key = uid << 8 | side
    g_betting_batch_list[key] = (g_betting_batch_list[key] or 0) + betting_coins
end

function check_and_sync_betting_action()
    if not next(g_betting_batch_list) then
        return
    end

    local betting_info_list = {}
    for key,betting_coins in pairs(g_betting_batch_list) do
        local uid = key >> 8
        local side = key & 0xFF
        g_betting_batch_list[key] = nil
        local curr_coins
        local player_info = player_info_list[uid]
        if player_info then
            curr_coins = player_info.coins
        end
        local bet_index = get_bet_index(player_info.available_betting_list,betting_coins)

        table_insert(betting_info_list,{
            uid = uid,
            coins = betting_coins,
            curr_coins = curr_coins,
            side = side,
            bet_index = bet_index,
        })
    end
    
    local tmp_list = curr_hhdz_instance:get_all_betting_list()
    local all_betting_list = {}
    for side,coins in pairs(tmp_list) do
        table_insert(all_betting_list,{side = side,coins = coins})
    end

    local ntf = {
        betting_info_list = betting_info_list,
        betting_coins_list = all_betting_list,
    }
    --print_r(ntf)
    notify_all('honghei.NTF_BET',ntf)

    return true
end

local function routine_sync_betting_action()
    while true do
        local ok,ret = xpcall(check_and_sync_betting_action,debug.traceback)
        if not ok then
            errlog(ret)
        end
        skynet.sleep(30)
    end
end

function handler.REQ_BET(uid,msg,game_session)
   -- print("REQ_BET==========",tostring_r(msg))
    
    if table_players[uid] ~= game_session then
        errlog(uid,'invalid status',table_players[uid],game_session)
        return false
    end

    --必须是可押注状态
    if curr_status ~= TABLE_STATUS_BETTING then
        -- errlog(uid,"bet curr_status:",curr_status)
        send_to_gateway(uid,game_session,'honghei.RSP_BET',{result = -200})
        return
    end

    -- --前一秒停止押注吧
    -- local curr_time = util.get_now_time()
    -- if curr_time - game_start_betting_time >= CLIENT_GAME_STATUS_BETTING_TIME then
    --     send_to_gateway(uid,game_session,'honghei.RSP_BET',{result = -201})
    --     return
    -- end

    --取出玩家对象先
    local player_info = player_info_list[uid]
    if not player_info then
        errlog(uid,'this guy is not in table')
        send_to_gateway(uid,game_session,'honghei.RSP_BET',{result = -201})
        return
    end
    start_coins_map[uid] = start_coins_map[uid] or player_info.coins
    local start_coins = start_coins_map[uid] 
   -- print("hhdz_start_coins====",uid,start_coins)

    local roomdata = global_configs.roomdata[self_table_type]
    local hhdz_min_bet = roomdata.min_bet or 0
   
    if start_coins < hhdz_min_bet then
        -- errlog(uid,"bet start_coins:",start_coins,"hhdz_min_bet:",hhdz_min_bet)
        local res = utils.getErrResult(error_code.HHDZ_BET_MIN_COINS_LIMIT, hhdz_min_bet)
        send_to_gateway(uid, game_session, "honghei.RSP_BET", res)
        return true
    end

    local side = msg.side
    local betting_coins = msg.betting_coins
    local bet_type = msg.bet_type
    local robot = is_robot(uid)
  --  print_r(player_info)
    local ok,ret = curr_hhdz_instance:can_bet(uid,side,betting_coins,player_info.available_betting_list,bet_type,self_table_type)
    if not ok then 
        -- if ret == -300 then
        --     ret = error_code.CANT_BET_DIFFERENT_SIDE
        -- end
        -- errlog(uid,"bet ret:",ret,"ok:",ok)
        send_to_gateway(uid,game_session,'honghei.RSP_BET',{result = ret})
        return
    end

    uid_betting_map[uid] = true

    local ok,succ,ret
    --去扣钱
    if robot then
        ok,ret = curr_hhdz_instance:bet(uid,side,betting_coins,is_robot(uid),player_info,bet_type,self_table_type)
        if not ok then
            errlog(uid,'failed to betting coins',betting_coins)
            send_to_gateway(uid,game_session,'honghei.RSP_BET',{result = ret})
            return
        end
        player_info.coins = player_info.coins - betting_coins
    else
        ok,succ,ret = R().basesvr{key=uid}:call('.msg_handler',
            'reduce_coins',uid,betting_coins,reason.BET_COIN,{is_robot=robot},get_extra_bill_info(uid))
        if not ok or not succ then
            errlog(uid,'failed to reduce coins',ok,succ,betting_coins)
            send_to_gateway(uid,game_session,'honghei.RSP_BET',
                {result = error_code.GOLD_IS_NOT_ENOUGH})
            return
        end

        --异步完后，必须是可押注状态
        if curr_status ~= TABLE_STATUS_BETTING then
            errlog(uid,'failed to check status',betting_coins,curr_status)
            --如果此时不可押注，则返回钱
            R().basesvr{key=uid}:call('.msg_handler','add_coins',uid,betting_coins,reason.BET_COIN_BACK,
                {is_robot=robot},get_extra_bill_info(uid))
            send_to_gateway(uid,game_session,'honghei.RSP_BET',{result = -113})
            return
        end


        local curr_coins = ret.curr
        ok,ret = curr_hhdz_instance:bet(uid,side,betting_coins,is_robot(uid),player_info,bet_type,self_table_type)
        if not ok then
            errlog(uid,'failed to betting coins',betting_coins)
            --如果此时不可押注，则返回钱
            R().basesvr{key=uid}:call('.msg_handler','add_coins',uid,betting_coins,reason.BET_COIN_BACK,
                {is_robot=robot},get_extra_bill_info(uid))
            send_to_gateway(uid,game_session,'honghei.RSP_BET',{result = ret})
            return
        end

        player_info.coins = curr_coins
        R().basesvr{key=uid}:send('.msg_handler','add_lose_coins',uid,betting_coins)
    end
    collect_betting_action(uid,player_info,side,betting_coins)

    --通知玩家本人金币发生变化
    utils.notify_money_changed(uid,{coins = player_info.coins},game_session,
        constant.MONEY_CHANGED_REASON.HHDZ_BET)

    send_to_gateway(uid,game_session,'honghei.RSP_BET',{
        side = side,
        betting_coins = betting_coins,
        side_coins = ret.side_coins,
        my_side_coins = ret.my_side_coins,
    })

    return true
end

function handler.REQ_SITDOWN(uid,msg,game_session)
    dbglog("REQ_SITDOWN game_session:",game_session)
    if table_players[uid] ~= game_session then
        errlog(uid,'invalid status',table_players[uid],game_session)
        return false
    end

    local position = msg.position
    if ordered_players[position] ~= EMPTY_SEAT then
        send_to_gateway(uid,game_session,'honghei.RSP_SITDOWN',{
            result = error_code.CANT_SITDOWN
        })
        return
    end

    --外部不能请求入座特殊位置
    if position == hhdz.RICH_SEAT_SIDE or pos == hhdz.LUCK_SEAT_SIDE then
        send_to_gateway(uid, game_session, "honghei.RSP_SITDOWN", { result = error_code.SEAT_SPECIAL_SIDE })
        return true
    end

    local ok, ret, sitdown_coin = sit_down(uid,position)
    if not ok then
        errlog(uid,'failed to sit down',position,ret)
        local res = utils.getErrResult(ret, sitdown_coin)
        send_to_gateway(uid,game_session,'honghei.RSP_SITDOWN', res)
        return
    end

    send_to_gateway(uid,game_session,'honghei.RSP_SITDOWN',{
        position = position
    })
    return true
end

function handler.REQ_STANDUP(uid,msg,game_session)
    if table_players[uid] ~= game_session then
        errlog(uid,'invalid status',table_players[uid],game_session)
        return false
    end

    local position = msg.position
    if ordered_players[position] == EMPTY_SEAT then
        send_to_gateway(uid,game_session,'honghei.RSP_STANDUP',{
            result = error_code.CANT_STANDUP
        })
        return
    end

    stand_up(uid,position)

    send_to_gateway(uid,game_session,'honghei.RSP_STANDUP',{
        position = position
    })

    return true
end

function handler.REQ_CHAT(uid,msg)
    local player_info = assert(player_info_list[uid])
    local curr_time = util.get_now_time()

    if player_info.last_chat_time and curr_time - player_info.last_chat_time < 1 then
        send_to_gateway(uid,table_players[uid],'table.RSP_CHAT',
            {result = error_code.REQ_CHAT_TOO_FAST})
        return false
    end

    player_info.last_chat_time = curr_time

    send_to_gateway(uid,table_players[uid],'table.RSP_CHAT',{result = error_code.RSP_SUCESS})

    local ntf = {
        uid = uid,
        type = msg.type,
        picture_id = msg.picture_id,
        content = msg.content,
        voice = msg.voice,
        content_id = msg.content_id,
        name = player_info.name,
        icon = player_info.icon,
        sex = player_info.sex,
    }

    notify_all('table.NTF_CHAT',ntf)

    return true
end

--玩家请求离开房间
function handler.REQ_LEAVE(uid,msg,game_session)
    if table_players[uid] ~= game_session then
        errlog(uid,'invalid status',table_players[uid],game_session)
        return false
    end

    local player_info = player_info_list[uid]
    if not player_info then
        dbglog(uid,'player_info is nil')
        return false
    end

    -- local init_coins = player_info.init_coins or 0
    -- local robot = is_robot(uid) --leave会把机器人标记置空所以需要先取机器人标记
    local ret = leave(uid)

    send_to_gateway(uid,game_session,'honghei.RSP_LEAVE',{})
    -- watch_session(game_session,uid,false)
    -- skynet.send('.table_mgr','lua','leave',uid)

    -- if ret and not robot then
    --     R().hallsvr({key=uid}):send('.msg_handler', 'check_lucky_tigger',uid,self_game_type,init_coins)
    -- end

    return true
end

function handler.REQ_PLAYER_LIST(uid,msg,game_session)
    if table_players[uid] ~= game_session then
        errlog(uid,'invalid status',table_players[uid],game_session)
        return false
    end

    local rsp_player_info_list = {}
    local luck_uid = ordered_players[hhdz.LUCK_SEAT_SIDE]
    local rich_uid = ordered_players[hhdz.RICH_SEAT_SIDE]

    for uid, player_info in pairs(player_info_list) do
        if uid ~= luck_uid and uid ~= rich_uid then
            local rsp_player_info = get_response_player_info(player_info)
            table_insert(rsp_player_info_list, rsp_player_info)
        end
    end 
    --下注金额→获胜局数→玩牌局数
    table_sort(rsp_player_info_list,function(a,b)
        if a.total_bet ~= b.total_bet then
            return a.total_bet > b.total_bet
        elseif a.win_count ~= b.win_count then
            return a.win_count > b.win_count
        else
            return a.play_count < b.play_count
        end
    end)

    --运气王置顶显示
    local luck_info = player_info_list[luck_uid]
    local rich_info = player_info_list[rich_uid]
    if luck_info then
        table_insert(rsp_player_info_list,1,get_response_player_info(luck_info))
    end
    if rich_info then
        if #rsp_player_info_list == 0 then
            table_insert(rsp_player_info_list,get_response_player_info(rich_info))
        else
            table_insert(rsp_player_info_list,2,get_response_player_info(rich_info))
        end
    end

    send_to_gateway(uid,game_session,'honghei.RSP_PLAYER_LIST',{
        player_info_list = rsp_player_info_list
    })

    return true
end

function handler.REQ_TREND_LIST(uid,msg,game_session)
    if table_players[uid] ~= game_session then
        errlog(uid,'invalid status',table_players[uid],game_session)
        return false
    end

    local winner_side_list = {}
    local winner_type_list = {}

    for _,h in pairs(g_history_list) do
        -- if #winner_side_list >= 26 then
        --     break
        -- end            
        table_insert(winner_side_list,1,h.side)
    end
    for _,h in pairs(g_history_list) do
        -- if #winner_type_list >= 24 then
        --     break
        -- end
        table_insert(winner_type_list,1,h.type)
    end

    local result_pailu_list = {}
    if #g_history_list > 0 then
        local pailu_list = {}
        local last_win_num = 0
        local last_win_side = g_history_list[1].side
        local i = 1
        while i <= #g_history_list do --and #pailu_list < 20 do
            local h = g_history_list[i]
            if last_win_side == h.side then
                last_win_num = last_win_num + 1
                i = i + 1
            else
                table_insert(pailu_list,1,last_win_side << 16 | last_win_num)
                last_win_num = 0
                last_win_side = h.side
            end
        end
        if last_win_num > 0 then
            table_insert(pailu_list,1,last_win_side << 16 | last_win_num)
        end

        local i = 1
        local curr_pailu = pailu_list[i]
        while i <= #pailu_list do
            local side = curr_pailu >> 16
            local num = curr_pailu & 0xFFFF
            if num <= 6 then
                i = i + 1
                curr_pailu = pailu_list[i]
            else
                curr_pailu = side << 16 | (num - 6)
                num = 6
            end
            table_insert(result_pailu_list,side * 10 + num)
        end
        -- while #result_pailu_list > 20 do
        --     table_remove(result_pailu_list,1)
        -- end
    end

    send_to_gateway(uid,game_session,'honghei.RSP_TREND_LIST',{
        winner_side_list = winner_side_list,
        pailu_list = result_pailu_list,
        winner_type_list = winner_type_list,
    })

    return true
end

function handler.REQ_USE_MAGIC_PICTURE(uid,msg,game_session)
    local dst_id = msg.dst_id
    local picture_id = msg.picture_id
    if table_players[uid] ~= game_session then
        errlog(uid,'invalid status',table_players[uid],game_session)
        return
    end

    local src_player_info = player_info_list[uid]
    if not src_player_info then
        errlog(uid,"REQ_USE_MAGIC_PICTURE failed you are not a src_player_info")
        return
    end

    -- if not src_player_info.position then
    --     send_to_gateway(uid,game_session,'table.RSP_USE_MAGIC_PICTURE',{result = error_code.USE_ITEM_NEED_POSTION})
    --     return
    -- end

    local dst_player_info = player_info_list[dst_id]
    if not dst_player_info then
        errlog(dst_id,"failed you are not a dst_player_info")
        send_to_gateway(uid,game_session,'table.RSP_USE_MAGIC_PICTURE',{
            result = error_code.TABLE_USE_MAGIC_PICTURE_MUST_SITDOWN
        })
        return
    end

    if not dst_player_info.position then
        errlog(uid,"player not have position")
        send_to_gateway(uid,game_session,'table.RSP_USE_MAGIC_PICTURE',{
            result = error_code.TABLE_USE_MAGIC_PICTURE_MUST_SITDOWN
        })
        return
    end
    -- local coin_cost = constant.USE_MAGIC_PICTURE_COIN
    -- local ok,succ,ret = R().basesvr{key=uid}:call('.msg_handler','reduce_coins',uid,
    --     coin_cost,reason.USE_MAGIC_PICTRUE,get_extra_bill_info(uid))
    -- if not ok then
    --     errlog(uid, "failed to reduce_coins",coin_cost)
    --     send_to_gateway(uid,game_session,'table.RSP_USE_MAGIC_PICTURE',{result = -1})
    --     return
    -- end

    -- if not succ then
    --     errlog(uid, "failed to reduce_coins",coin_cost)
    --     send_to_gateway(uid,game_session,'table.RSP_USE_MAGIC_PICTURE',{
    --         result = error_code.GOLD_IS_NOT_ENOUGH})
    --     return
    -- end

    -- src_player_info.coins = ret.curr
    send_to_gateway(uid,table_players[uid],'table.RSP_USE_MAGIC_PICTURE',{})

    notify_all('table.NTF_USE_MAGIC_PICTURE',{
        src_id = uid,
        dst_id = dst_id,
        picture_id = picture_id,
    })

    --utils.notify_money_changed(uid,{coins = ret.curr},game_session)
    
    return true
end


--换游戏
-- function handler.REQ_GAME_CHANGE(uid,msg,game_session)
--     local roomdata_list = global_configs.roomdata or {}
--     local roomdata = roomdata_list[msg.table_type]
--     if not roomdata then
--         errlog(self_table_type,"check_game_change_table but roomdata is nil")
--         return false
--     end

--     local player_info = player_info_list[uid]
--     if not player_info then
--         dbglog("REQ_GAME_CHANGE error player_info is nil", uid)
--         send_to_gateway(uid,game_session,'table.RSP_GAME_CHANGE',{result = error_code.PERMISSION_DENIDE})
--         return false
--     end

--     if uid_betting_map[uid] then --游戏中不可以换游戏
--         send_to_gateway(uid,game_session,'table.RSP_GAME_CHANGE',{result = error_code.IN_GAME_CAN_NOT_CHANGE})
--         return false
--     end

--     if player_info.coins < (roomdata.min or 0) then
--         send_to_gateway(uid,game_session,'table.RSP_GAME_CHANGE',{result = error_code.GOLD_IS_NOT_ENOUGH})
--         return false
--     end

--     if (roomdata.max or 0) ~= 0 and player_info.coin > (roomdata.max or 0) then
--         send_to_gateway(uid,game_session,'table.RSP_GAME_CHANGE',{result = error_code.GOLD_OUT_OF_MAX})
--         return false
--     end
--     print("change_table_type=============",msg.table_type)

--     local ret = leave(uid)
--     if not ret then 
--        send_to_gateway(uid, game_session, "table.RSP_GAME_CHANGE", { result = error_code.RSP_FAIL })
--        return false
--     end

--     local user_data = {
--         uid = uid,
--         table_type = msg.table_type,
--         begin_time = util.get_now_time(),
--         name = player_info.name,
--         coins = player_info.coins,
--         sex = player_info.sex,
--         icon = player_info.icon,
--         --old_table_gid = self_table_gid,
--         --ip_limit = false,
--         --login_ip = player_info.login_ip,
--     }   

--     --请求匹配服进行匹配了
--     local ok = R().newmatchsvr(1):call(".table_mgr", "match_player",user_data)
--     if not ok then
--         errlog(string_format("uid(%d) call newmatchsvr table_mgr match_player error",uid))
--         send_to_gateway(uid, game_session, "table.RSP_GAME_CHANGE", { result = error_code.RSP_FAIL })
--         return true
--     end
--     send_to_gateway(uid, game_session, "table.RSP_GAME_CHANGE", { result = error_code.RSP_SUCESS })

--     return true
-- end

function internal.get_trend_info()
    local roomdata = global_configs.roomdata[self_table_type]
    local result = {}
    if roomdata.person_limit then
        result.min = roomdata.person_limit[1] or 0
        result.max = roomdata.person_limit[2] or 0
    end
    result.winner_side_list = {}
    result.card_type_list = {}
    result.curr_status,result.left_time,result.status_time = get_client_status_and_left_time()
    local curr_time = util.get_now_time()
    --dbglog("get_trend_info curr_time:",curr_time,"result.left_time ",result.curr_status,result.left_time,"curr_status",curr_status)
    result.name = roomdata.name
    result.table_type = self_table_type
    result.game_type = game_def.get_game_type(self_table_type)
    result.eject_rate = roomdata.eject_rate
    result.eject_time = roomdata.eject_time
    for _,h in pairs(g_history_list) do
        if #result.winner_side_list >= 72 then
            break
        end            
        table_insert(result.winner_side_list,1,h.side)
        table_insert(result.card_type_list,1,h.type)
    end
    if TABLE_STATUS_GAMEOVER_WAIT == curr_status then
        table_remove(result.winner_side_list,#result.winner_side_list)
        table_remove(result.card_type_list,#result.card_type_list)
    end
    return result 
end

--在线玩家概况数据
function internal.get_summary_online()
    local res = {}
    for uid, player in pairs(player_info_list) do 
        if not is_robot(uid) then 
            table_insert(res, {
                uid = uid, 
                all_bet = player.all_bet, --总投注
                all_win_lose = player.all_win_lose, --输赢
                all_free = player.all_free, --台费
                all_produce = player.all_produce, --总产出
            })
        end
    end
    return res
end

-----------------------掉线回调接口-----------------------
function internal.disconnect(uid,game_session)
    dbglog(uid,'disconnect...',table_players[uid],game_session)
    if table_players[uid] ~= game_session then
        errlog(uid,'invalid status',table_players[uid],game_session)
        return
    end

    local ret = leave(uid)
    dbglog(uid,'disconnect...',ret)
    return true
end

function internal.stop_server()
    stopping_server = true
end

-- 关服回调接口
function internal.close_server()
    closing_server = true
end


local function update_player_coins(uid, coins)
    local player_info = player_info_list[uid]
    if player_info then
        player_info.coins = coins
    else
        errlog(string_format("uid(%d) coins(%d) player info not exist", uid, coins))
    end
end

local function check_have_enough_coins(uid,coins)
    local player_info = player_info_list[uid]
    if not player_info or player_info.coins < coins then
        return false
    end
    return true
end

local function caishen_coins(uid, coins, award_coins, ...)
    local robot = is_robot(uid)
    local ok, succ, ret = R().basesvr({ key = uid }):call(".msg_handler", "caishen_coins", uid, coins, award_coins, {is_robot=robot},get_extra_bill_info(uid))
    if not ok then
        errlog(string_format("uid(%d) call basesvr caishen_coins coins error", uid))
        return false
    end
    if not succ then
        errlog(string_format("uid(%d) basesvr caishen_coins coins succ == false error", uid))
        return false
    end

    --update_player_coins(uid, ret.curr)
    return ret
end

local function pay_caishen_coins(uid,coins)
    local caishenjiadao_store = global_configs.caishenjiadao_store
    local fee_rate = caishenjiadao_store.fee_rate
    local sysback_coins = math_floor((coins *fee_rate) / 10000)
    local base_coins = coins - sysback_coins
    local curr_coins
    local ret_coins = system_store.check_caishen_store_result(self_table_type,uid,coins)
    local player_info = player_info_list[uid]
    player_info.all_bet = player_info.all_bet + coins
    local produce = 0
    local win_lose = 0
    if ret_coins then
        print("add coins",ret_coins)
        curr_coins = caishen_coins(uid,coins,ret_coins)
        if not curr_coins then
            errlog("reduce coins failed",ret )
            return error_code.NOT_ENOUGH_COINS
        end
        sendLoseWinToFriendServer(coins - ret_coins)
        --改变财神库存
        R().exdbsvr(1):send('.caishen_mgr','push_data_pool',self_table_type,uid,base_coins,sysback_coins)
        R().exdbsvr(1):send('.caishen_mgr','sub_caishen_base',self_table_type,uid,ret_coins)
        player_info.all_win_lose = player_info.all_win_lose - coins + ret_coins
        player_info.all_produce = player_info.all_produce + ret_coins
        produce = ret_coins       
        win_lose =  - coins + ret_coins          
    else
        local ret = reduce_coins(uid, coins, reason.OPEN_CAISHEN)
        if not ret then
            errlog("reduce coins failed",ret )
            return error_code.NOT_ENOUGH_COINS
        end
        sendLoseWinToFriendServer(coins)        
        --改变财神库存
        R().exdbsvr(1):send('.caishen_mgr','push_data_pool',self_table_type,uid,base_coins,sysback_coins)
        curr_coins = ret.curr
        player_info.all_win_lose = player_info.all_win_lose - coins        
        win_lose =  - coins          
    end
    local uid = uid
    local bet_coins = coins
    local produce = produce
    local win_lose = win_lose
    local free = 0
    expand.sendSummary(self_table_type, uid, bet_coins, produce, win_lose, free)


    utils.notify_money_changed(uid,{coins = curr_coins})
    notify_all("table.NTF_MONEY_CHANGED", { chged_list = { { uid = uid, coins = curr_coins } } })
    
    update_player_coins(uid, curr_coins)
    caishen_coins_map[uid] = nil
    return error_code.RSP_SUCESS,ret_coins    
end

local function add_coins(uid, money, reason, ...)
    local ok, succ, ret = R().basesvr({ key = uid }):call(".msg_handler", "add_coins", uid, money, reason, ...)
    if not ok then
        errlog(string_format("uid(%d) call basesvr reduce coins error", uid))
        return false
    end
    if not succ then
        errlog(string_format("uid(%d) basesvr reduce coins succ == false error", uid))
        return false
    end

    --update_player_coins(uid, ret.curr)
    return ret
end

function handler.REQ_OPEN_CAISHEN(uid,msg)
    local coins = msg.coins
    if not caishen_coins_map[uid] or caishen_coins_map[uid] ~= coins then
        errlog("put coins error",uid,caishen_coins_map[uid])
        send_to_gateway(uid,table_players[uid],'table.RSP_OPEN_CAISHEN',{result = error_code.INPUT_ERROR})
        return 
    end
    if not check_have_enough_coins(uid,coins) then
        errlog("you have not enough coins",uid)
        send_to_gateway(uid,table_players[uid],'table.RSP_OPEN_CAISHEN',{result = error_code.NOT_ENOUGH_COINS})
        return false
    end

    local res,ret_coins = pay_caishen_coins(uid,coins)
    if res == error_code.RSP_SUCESS then
        send_to_gateway(uid,table_players[uid],'table.RSP_OPEN_CAISHEN',{result = res,award_coins = ret_coins})
    else
        send_to_gateway(uid,table_players[uid],'table.RSP_OPEN_CAISHEN',{result = res})
    end
end

function handler.REQ_PERSONAL_INFO(uid,msg,game_session)
    local player_info = player_info_list[msg.uid]
    local ret = {}
    if player_info then
        local personal_info = {
            uid = player_info.uid,
            name = player_info.name,
            sex = player_info.sex,
            icon = player_info.icon,
            coins = player_info.coins,
            is_self = false,
            vip_level = player_info.vip_level or 0,
            icon_border = player_info.icon_border,
        }
        ret.personal_info = personal_info
        ret.is_set_down = player_info.position and true or false
        send_to_gateway(uid,game_session,'table.RSP_PERSONAL_INFO',ret)
    else
        send_to_gateway(uid,game_session,'table.RSP_PERSONAL_INFO',{result = error_code.NO_SUCH_PLAYER})
    end
    return true
end

local function notify_player_enter(uid)
    local player_info = player_info_list[uid]
    local player = get_response_player_info(player_info)

    print("notify_player_enter22222222222",tostring_r(player))
    local play_effect = false
    local enter_word = ""
    local vip_level_conf = utils.getDataBykv(global_configs.vip,"level",player_info.vip_level or 0)
    if vip_level_conf and vip_level_conf.privilege[game_def.VIP_PRIVILEGE_TYPE_SIX] then
        play_effect = true
        enter_word = vip_level_conf.enter_word or ""
    end
    if uid_betting_map[uid] then --下注过就是断线重连
        play_effect = false
    end
    notify_all('honghei.NTF_PLAYER_ENTER',{play_effect = play_effect,player = player,enter_word = enter_word})
end

-- 客户端其请求进入房间
function internal.enter(uid,game_session,msg)
    local ok,r = enter(uid,game_session,msg)
    if not ok then
        send_to_gateway(uid,game_session,'table.RSP_ENTER',{result = r})
        return false
    end

    send_to_gateway(uid,game_session,'table.RSP_ENTER',r)

    --跟网关agent绑定seesion监控 网关有关闭事件进行通知
    watch_session(game_session,uid,true)
    if not is_robot(uid) then
        R().friendsvr(1):send('.info_mgr','update_info',uid,{curr_game_type = self_game_type, curr_table_type = self_table_type})
    end
    print("hhdz_notify_player_enter========")
    notify_player_enter(uid)

    return true
end

function internal.touch(key)

end

-- 服务开启设置房间参数
function internal.start(conf)
    dbglog(tostring_r(conf))
    tablesvr_id = tonumber(skynet.getenv "server_id")
    self_table_id = conf.table_id
    self_table_type = conf.table_type
    this_table_gid = conf.table_gid
    self_game_type = conf.game_type
    self_table_pattern = conf.table_conf.table_pattern
    curr_round = 0
    print("self_pattern",self_table_pattern)
    --先填充所有的有座玩家
    init_position()

    self_game_config = global_configs.roomdata[self_table_type]

    curr_status = TABLE_STATUS_WAITTING
    game_start_wait_time = util.get_now_time()
    game_create_time = util.get_now_time()
    local randnum = math_random(1,72)
    g_history_list = hhdz.get_random_history(randnum)
    g_betting_batch_list = {}

    gen_card_instance = gen_card:new()
    -- gen_card_instance:init()

    dbglog(string.format('table<%d>,type(%d),table gid(%d) got start',
        self_table_id,self_table_type,this_table_gid))
    skynet.fork(game_update)
    skynet.fork(routine_sync_betting_action)

    return true
end

function internal.exit()
    skynet.exit()
end

function internal.update_coins_on_table(uid,amount,safe_box)
    local player_info = player_info_list[uid]
    if player_info then
        player_info.coins = player_info.coins + amount
        start_coins_map[uid] = player_info.coins
        local chged_list = {}
        table_insert(chged_list,{uid=uid,coins=player_info.coins})
        if player_info.position then
            notify_all('table.NTF_MONEY_CHANGED',{chged_list=chged_list})
        else
            send_to_gateway(uid,table_players[uid],'table.NTF_MONEY_CHANGED',{chged_list = chged_list})
        end
        if safe_box then
            player_info.safe_box = safe_box
        end
    end
    return true
end

function internal.newmatch_register_all(player_data_list)
    local result = {}
    local failed_list = {}
    local success_list = {}

    local end_idx
    for i,player_data in pairs(player_data_list) do
        local uid = player_data.uid
        table_players[uid] = REGISTER_CLIENT_FD
        table_insert(success_list,uid)
    end

    result.failed_list = failed_list
    result.success_list = success_list
    local table_stats = {}
    table_stats.table_type = self_table_type
    table_stats.left_num = table_def.game_player_num[self_game_type] - get_player_num()
    table_stats.table_gid = this_table_gid
    table_stats.game_type = self_game_type
    table_stats.table_pattern = self_table_pattern
    result.table_stats = table_stats
    return result
end

--=================================table protocols=================
local function update_player_heartbeat(uid)
    local player_info = player_info_list[uid]
    if player_info then
        player_info.last_ping_time = util.get_now_time()
    end
end

local function get_msg_module_name(msgid)
    local m = msgdef.id_to_name[msgid]
    if not m then return end
    return m[1],m[2] --[1]->module,[2]->name
end

function dispatch_client_message(game_session,uid,msg,size)
    local _,msgid,pbmsg,pbsize = proxypack.unpack_client_message(msg,size)
    local module,name = get_msg_module_name(msgid)

    if not module or not name then
        print('invalid msgid',msgid,module,name)
        return
    end

    local pbname = module .. '.' .. name
    local req_msg = pb.decode(pbname,pbmsg,pbsize)

    dbglog(string.format('[%s]>>>>>player[%s] got a request[%s] content(%s)',
        skynet.address(skynet.self()),tostring(uid),pbname,tostring_r(req_msg)))

    local f = handler[name]
    if not f then
        errlog('unknown action',pbname)
        return
    end

    local ret = f(uid,req_msg,game_session)
    if not ret then
        errlog(string.format('failed to handle requrest(%s.%s)',module,name))
    end

    update_player_heartbeat(uid)
end

local function dispatch(_,_,game_session,uid,data)
    local msg,size = proxypack.pack_raw(data)
    local ok,errmsg = xpcall(dispatch_client_message,debug.traceback,game_session,uid,msg,size)
    skynet.trash(msg,size)  --这里需要保证内存被释放
    if not ok then 
        errlog(errmsg)
    end
end

function handle_lua_req(session,source,action,...)
   -- dbglog('internal request',action,...)
    local f = internal[action]
    if f then
        skynet.retpack(f(...))
    else
        handler[action](...)
    end
end

function start()
    skynet.register_protocol {
        name = "client",
        id = skynet.PTYPE_CLIENT,
        unpack = skynet.unpack,
        dispatch = dispatch
    }

    skynet.start(function()
        skynet.dispatch("lua",function(session,source,action,...)
            handle_lua_req(session,source,action,...)
        end)

       load_config()
    end)
end

return _ENV